utils.c
上传用户:knt0001
上传日期:2022-01-28
资源大小:264k
文件大小:8k
源码类别:

Email客户端

开发平台:

C/C++

  1. /**
  2.     eMail is a command line SMTP client.
  3.     Copyright (C) 2001 - 2008 email by Dean Jones
  4.     Software supplied and written by http://www.cleancode.org
  5.     This file is part of eMail.
  6.     eMail is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.     eMail is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.     You should have received a copy of the GNU General Public License
  15.     along with eMail; if not, write to the Free Software
  16.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17. **/
  18. #if HAVE_CONFIG_H
  19. # include "config.h"
  20. #endif
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <string.h>
  25. #include <ctype.h>
  26. #include <errno.h>
  27. #include <pwd.h>
  28. #include <sys/stat.h>
  29. #include <sys/wait.h>
  30. #include <sys/utsname.h>
  31. /* Autoconf manual suggests this. */
  32. #if TIME_WITH_SYS_TIME
  33. # include <sys/time.h>
  34. # include <time.h>
  35. #else
  36. # if HAVE_SYS_TIME_H
  37. #  include <sys/time.h>
  38. # else
  39. #  include <time.h>
  40. # endif
  41. #endif
  42. #include "email.h"
  43. #include "utils.h"
  44. #include "error.h"
  45. #include "mimeutils.h"
  46. /**
  47.  * Return number of printable chars in a utf8 string
  48.  */
  49. size_t
  50. utf8Strlen(const u_char *str)
  51. {
  52. size_t len=0;
  53. while (*str != '') {
  54. u_char fbyte = str[0];
  55. /* If greater than 0x7F (127) then it's not normal ASCII */
  56. if (fbyte > 0x7F) {
  57. /* It's a 2-byte sequence if it's between 0xC0(192) 
  58.    and 0xDF(223),
  59.    It's a 3-byte sequence if it's between 0xE0(224)
  60.    and 0xEF(239) 
  61.    It's a 4-byte sequence if it's between 0xF0(240)
  62.    and 0xF4(244) */
  63. if (fbyte >= 0xC0 && fbyte <= 0xDF) {
  64. str += 2;
  65. } else if (fbyte >= 0xE0 && fbyte <= 0xEF) {
  66. str += 3;
  67. } else if (fbyte >= 0xF0 && fbyte <= 0xF4) {
  68. str += 4;
  69. } else {
  70. str += 1;
  71. }
  72. len++;
  73. } else {
  74. len++;
  75. }
  76. }
  77. return len;
  78. }
  79. /**
  80.  * We're going to try and get the type of character set this
  81.  * string is.  We primarily just support UTF-8 and ASCII right
  82.  * now.  May support other charsets later.
  83.  */
  84. CharSetType
  85. getCharSet(const u_char *str)
  86. {
  87. CharSetType type=IS_ASCII;
  88. u_int utf8=0, ascii=0;
  89. u_int percent_ascii=0;
  90. while (*str != '') {
  91. u_char fbyte = str[0];
  92. /* If greater than 0x7F (127) then it's not normal ASCII */
  93. if (fbyte > 0x7F) {
  94. /* It's a 2-byte sequence if it's between 0xC0(192) 
  95.    and 0xDF(223),
  96.    It's a 3-byte sequence if it's between 0xE0(224)
  97.    and 0xEF(239) 
  98.    It's a 4-byte sequence if it's between 0xF0(240)
  99.    and 0xF4(244) */
  100. if (fbyte >= 0xC0 && fbyte <= 0xDF) {
  101. str += 2;
  102. } else if (fbyte >= 0xE0 && fbyte <= 0xEF) {
  103. str += 3;
  104. } else if (fbyte >= 0xF0 && fbyte <= 0xF4) {
  105. str += 4;
  106. } else {
  107. str += 1;
  108. }
  109. utf8++;
  110. } else {
  111. ascii++;
  112. str += 1;
  113. }
  114. }
  115. if (utf8) {
  116. /* If the string is 75% or more of ascii characters, 
  117.    we'll call it partial utf-8 */
  118. if (ascii > 0) {
  119. percent_ascii = ((float)ascii / (float)(ascii + utf8)) * 100;
  120. }
  121. if (percent_ascii >= 75) {
  122. type = IS_PARTIAL_UTF8;
  123. } else {
  124. type = IS_UTF8;
  125. }
  126. } else if (!utf8 && !ascii) {
  127. type = IS_OTHER;
  128. }
  129. return type;
  130. }
  131. dstrbuf *
  132. encodeUtf8String(const u_char *str, bool use_qp)
  133. {
  134. const u_int max_blk_len = 45;
  135. u_int i=max_blk_len;
  136. dstrbuf *enc, *dsb = DSB_NEW;
  137. size_t len = strlen((char *)str);
  138. if (use_qp) {
  139. // TODO: We need to break this up so that we're not 
  140. // creating extra long strings.
  141. enc = mimeQpEncodeString(str, false);
  142. dsbPrintf(dsb, "=?utf-8?q?%s?=", enc->str);
  143. i = len; // Just reset for now.
  144. } else {
  145. enc = mimeB64EncodeString(str, 
  146. (len > max_blk_len ? max_blk_len : len), false);
  147. dsbPrintf(dsb, "=?utf-8?b?%s?=", enc->str);
  148. }
  149. dsbDestroy(enc);
  150. /* If we have anymore data to encode, we have to do it by adding a newline
  151.    plus a space because each section can only be 75 chars long. */ 
  152. while (i < len) {
  153. size_t newlen = strlen((char *)str + i);
  154. /* only allow max_blk_len sections */
  155. if (newlen > max_blk_len) {
  156. newlen = max_blk_len;
  157. }
  158. enc = mimeB64EncodeString(str + i, newlen, false);
  159. dsbPrintf(dsb, "rn =?utf-8?b?%s?=", enc->str);
  160. dsbDestroy(enc);
  161. i += newlen;
  162. }
  163. return dsb;
  164. }
  165. /**
  166.  * takes a string that is a supposed file path, and 
  167.  * checks for certian wildcards ( like ~ for home directory ) 
  168.  * and resolves to an actual absolute path.
  169. **/
  170. dstrbuf *
  171. expandPath(const char *path)
  172. {
  173. struct passwd *pw = NULL;
  174. dstrbuf *tmp = DSB_NEW;
  175. dstrbuf *ret = DSB_NEW;
  176. dsbCopy(tmp, path);
  177. if (tmp->len > 0 && tmp->str[0] == '&') {
  178. dsbCopy(ret, EMAIL_DIR);
  179. } else if (tmp->len > 0 && tmp->str[0] == '~') {
  180. if (tmp->str[1] == '/') {
  181. pw = getpwuid(getuid());
  182. } else {
  183. int pos = strfind(tmp->str, '/');
  184. if (pos >= 0) {
  185. char *p = substr(tmp->str, 1, pos-1);
  186. if (p) {
  187. pw = getpwnam(p);
  188. xfree(p);
  189. }
  190. }
  191. if (!pw) {
  192. pw = getpwuid(getuid());
  193. }
  194. }
  195. if (pw) {
  196. dsbCopy(ret, pw->pw_dir);
  197. }
  198. }
  199. if (ret->len > 0) {
  200. int pos = strfind(tmp->str, '/');
  201. if (pos > 0) {
  202. char *p = substr(tmp->str, pos, tmp->len);
  203. if (p) {
  204. dsbCat(ret, p);
  205. xfree(p);
  206. }
  207. }
  208. } else {
  209. dsbCopy(ret, path);
  210. }
  211. dsbDestroy(tmp);
  212. return ret;
  213. }
  214. int
  215. copyfile(const char *from, const char *to)
  216. {
  217. FILE *ffrom, *fto;
  218. dstrbuf *buf=NULL;
  219. ffrom = fopen(from, "r");
  220. fto = fopen(to, "w");
  221. if (!ffrom || !fto) {
  222. return -1;
  223. }
  224. buf = DSB_NEW;
  225. while (dsbFread(buf, MAXBUF, ffrom) > 0) {
  226. fwrite(buf->str, sizeof(char), buf->len, fto);
  227. dsbClear(buf);
  228. }
  229. dsbDestroy(buf);
  230. fclose(ffrom);
  231. fclose(fto);
  232. if (ferror(ffrom) || ferror(fto)) {
  233. return -1;
  234. }
  235. return 0;
  236. }
  237. /**
  238.  * checks to see if the TEMP_FILE is around... if it is
  239.  * it will move it to the users home directory as dead.letter.
  240. **/
  241. static void
  242. deadLetter()
  243. {
  244. dstrbuf *path = expandPath("~/dead.letter");
  245. FILE *out = fopen(path->str, "w");
  246. if (!out || !global_msg) {
  247. warning("Could not save dead letter to %s", path->str);
  248. } else {
  249. fwrite(global_msg->str, sizeof(char), global_msg->len, out);
  250. }
  251. dsbDestroy(path);
  252. }
  253. /**
  254.  * Gererate a string of random characters
  255. **/
  256. static const char letters[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" 
  257.                               "0123456789";
  258. #define SIZEOF_LETTERS 62
  259. dstrbuf *
  260. randomString(size_t size)
  261. {
  262. size_t i;
  263. long randval;
  264. struct timeval mill;
  265. dstrbuf *ret = DSB_NEW;
  266. gettimeofday(&mill, NULL);
  267. srand((getuid() + getpid()) + (mill.tv_usec << 16));
  268. for (i=0; i < size; i++) {
  269. randval = rand() / (RAND_MAX / SIZEOF_LETTERS);
  270. dsbCatChar(ret, letters[randval]);
  271. }
  272. return ret;
  273. }
  274. /**
  275.  * Get the first element from the Mopts.to list of emails
  276.  * and return it without the name or formating. just the
  277.  * email address itself.
  278. **/
  279. dstrbuf *
  280. getFirstEmail(void)
  281. {
  282. char *tmp=NULL;
  283. dstrbuf *buf = DSB_NEW;
  284. struct addr *a = (struct addr *)dlGetTop(Mopts.to);
  285. assert(a != NULL);
  286. /* If we haven't found a <, consider the e-mail unformatted. */
  287. tmp = strchr(a->email, '<');
  288. if (!tmp) {
  289. tmp = a->email;
  290. } else {
  291. /* strchr only brings us to the '<', Get past it */
  292. ++tmp;
  293. }
  294. dsbCopy(buf, tmp);
  295. tmp = strchr(buf->str, '>');
  296. if (tmp) {
  297. *tmp = '';
  298. }
  299. return buf;
  300. }
  301. /**
  302.  * Exit just handles all the signals and exiting of the 
  303.  * program by freeing the allocated memory  and writing 
  304.  * the dead.letter if we had a sudden interrupt...
  305. **/
  306. void
  307. properExit(int sig)
  308. {
  309. if (sig != 0 && global_msg) {
  310. deadLetter();
  311. }
  312. dsbDestroy(global_msg);
  313. /* Free lists */
  314. if (Mopts.attach) {
  315. dlDestroy(Mopts.attach);
  316. }
  317. if (Mopts.headers) {
  318. dlDestroy(Mopts.headers);
  319. }
  320. if (Mopts.to) {
  321. dlDestroy(Mopts.to);
  322. }
  323. if (Mopts.cc) {
  324. dlDestroy(Mopts.cc);
  325. }
  326. if (Mopts.bcc) {
  327. dlDestroy(Mopts.bcc);
  328. }
  329. dhDestroy(table);
  330. exit(sig);
  331. }
  332. int
  333. copyUpTo(dstrbuf *buf, int stop, FILE *in)
  334. {
  335. int ch;
  336. while ((ch = fgetc(in)) != EOF) {
  337. if (ch == '\') {
  338. ch = fgetc(in);
  339. if (ch == 'r') {
  340. ch = fgetc(in);
  341. }
  342. if (ch == 'n') {
  343. continue;
  344. }
  345. }
  346. if (ch == 'r') {
  347. ch = fgetc(in);
  348. if (ch == 'n') {
  349. return ch;
  350. }
  351. }
  352. if (ch == stop) {
  353. return ch;
  354. }
  355. dsbCatChar(buf, ch);
  356. }
  357. return ch;
  358. }