PageRenderTime 78ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/claws-mail-3.8.1/src/common/utils.c

#
C | 5297 lines | 4253 code | 744 blank | 300 comment | 1185 complexity | 4c6f146a0494bef88f6a540a9ee331dd MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
  3. * Copyright (C) 1999-2012 Hiroyuki Yamamoto & The Claws Mail Team
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program 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. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. # include "config.h"
  21. #endif
  22. #include "defs.h"
  23. #include <glib.h>
  24. #include <glib/gi18n.h>
  25. #ifdef USE_PTHREAD
  26. #include <pthread.h>
  27. #endif
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31. #include <errno.h>
  32. #include <sys/param.h>
  33. #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
  34. # include <wchar.h>
  35. # include <wctype.h>
  36. #endif
  37. #include <stdlib.h>
  38. #include <sys/stat.h>
  39. #include <unistd.h>
  40. #include <stdarg.h>
  41. #include <sys/types.h>
  42. #if HAVE_SYS_WAIT_H
  43. # include <sys/wait.h>
  44. #endif
  45. #include <dirent.h>
  46. #include <time.h>
  47. #include <regex.h>
  48. #ifdef G_OS_UNIX
  49. #include <sys/utsname.h>
  50. #endif
  51. #include <fcntl.h>
  52. #ifdef G_OS_WIN32
  53. # include <direct.h>
  54. # include <io.h>
  55. # include <w32lib.h>
  56. #endif
  57. #ifdef MAEMO
  58. #include <libosso.h>
  59. #ifdef CHINOOK
  60. # include <tablet-browser-interface.h>
  61. #else
  62. # include <osso-browser-interface.h>
  63. #endif
  64. #endif
  65. #include "utils.h"
  66. #include "socket.h"
  67. #include "../codeconv.h"
  68. #define BUFFSIZE 8192
  69. static gboolean debug_mode = FALSE;
  70. #ifdef G_OS_WIN32
  71. static GSList *tempfiles=NULL;
  72. #endif
  73. /* Return true if we are running as root. This function should beused
  74. instead of getuid () == 0. */
  75. gboolean superuser_p (void)
  76. {
  77. #ifdef G_OS_WIN32
  78. return w32_is_administrator ();
  79. #else
  80. return !getuid();
  81. #endif
  82. }
  83. #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
  84. gint g_chdir(const gchar *path)
  85. {
  86. #ifdef G_OS_WIN32
  87. if (G_WIN32_HAVE_WIDECHAR_API()) {
  88. wchar_t *wpath;
  89. gint retval;
  90. gint save_errno;
  91. wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
  92. if (wpath == NULL) {
  93. errno = EINVAL;
  94. return -1;
  95. }
  96. retval = _wchdir(wpath);
  97. save_errno = errno;
  98. g_free(wpath);
  99. errno = save_errno;
  100. return retval;
  101. } else {
  102. gchar *cp_path;
  103. gint retval;
  104. gint save_errno;
  105. cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
  106. if (cp_path == NULL) {
  107. errno = EINVAL;
  108. return -1;
  109. }
  110. retval = chdir(cp_path);
  111. save_errno = errno;
  112. g_free(cp_path);
  113. errno = save_errno;
  114. return retval;
  115. }
  116. #else
  117. return chdir(path);
  118. #endif
  119. }
  120. gint g_chmod(const gchar *path, gint mode)
  121. {
  122. #ifdef G_OS_WIN32
  123. if (G_WIN32_HAVE_WIDECHAR_API()) {
  124. wchar_t *wpath;
  125. gint retval;
  126. gint save_errno;
  127. wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
  128. if (wpath == NULL) {
  129. errno = EINVAL;
  130. return -1;
  131. }
  132. retval = _wchmod(wpath, mode);
  133. save_errno = errno;
  134. g_free(wpath);
  135. errno = save_errno;
  136. return retval;
  137. } else {
  138. gchar *cp_path;
  139. gint retval;
  140. gint save_errno;
  141. cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
  142. if (cp_path == NULL) {
  143. errno = EINVAL;
  144. return -1;
  145. }
  146. retval = chmod(cp_path, mode);
  147. save_errno = errno;
  148. g_free(cp_path);
  149. errno = save_errno;
  150. return retval;
  151. }
  152. #else
  153. return chmod(path, mode);
  154. #endif
  155. }
  156. FILE* g_fopen(const gchar *filename, const gchar *mode)
  157. {
  158. #ifdef G_OS_WIN32
  159. char *name = g_win32_locale_filename_from_utf8(filename);
  160. FILE* fp = fopen(name, mode);
  161. g_free(name);
  162. return fp;
  163. #else
  164. return fopen(filename, mode);
  165. #endif
  166. }
  167. int g_open(const gchar *filename, int flags, int mode)
  168. {
  169. #ifdef G_OS_WIN32
  170. char *name = g_win32_locale_filename_from_utf8(filename);
  171. int fd = open(name, flags, mode);
  172. g_free(name);
  173. return fp;
  174. #else
  175. return open(filename, flags, mode);
  176. #endif
  177. }
  178. #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
  179. #ifdef G_OS_WIN32
  180. gint mkstemp_name(gchar *template, gchar **name_used)
  181. {
  182. static gulong count=0; /* W32-_mktemp only supports up to 27
  183. tempfiles... */
  184. int tmpfd;
  185. *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
  186. tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
  187. (S_IRUSR | S_IWUSR));
  188. tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
  189. if (tmpfd<0) {
  190. perror(g_strdup_printf("cant create %s",*name_used));
  191. return -1;
  192. }
  193. else
  194. return tmpfd;
  195. }
  196. #endif /* G_OS_WIN32 */
  197. #ifdef G_OS_WIN32
  198. gint mkstemp(gchar *template)
  199. {
  200. gchar *dummyname;
  201. gint res = mkstemp_name(template, &dummyname);
  202. g_free(dummyname);
  203. return res;
  204. }
  205. #endif /* G_OS_WIN32 */
  206. void list_free_strings(GList *list)
  207. {
  208. list = g_list_first(list);
  209. while (list != NULL) {
  210. g_free(list->data);
  211. list = list->next;
  212. }
  213. }
  214. void slist_free_strings(GSList *list)
  215. {
  216. while (list != NULL) {
  217. g_free(list->data);
  218. list = list->next;
  219. }
  220. }
  221. static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
  222. {
  223. g_free(key);
  224. }
  225. void hash_free_strings(GHashTable *table)
  226. {
  227. g_hash_table_foreach(table, hash_free_strings_func, NULL);
  228. }
  229. gint str_case_equal(gconstpointer v, gconstpointer v2)
  230. {
  231. return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
  232. }
  233. guint str_case_hash(gconstpointer key)
  234. {
  235. const gchar *p = key;
  236. guint h = *p;
  237. if (h) {
  238. h = g_ascii_tolower(h);
  239. for (p += 1; *p != '\0'; p++)
  240. h = (h << 5) - h + g_ascii_tolower(*p);
  241. }
  242. return h;
  243. }
  244. void ptr_array_free_strings(GPtrArray *array)
  245. {
  246. gint i;
  247. gchar *str;
  248. cm_return_if_fail(array != NULL);
  249. for (i = 0; i < array->len; i++) {
  250. str = g_ptr_array_index(array, i);
  251. g_free(str);
  252. }
  253. }
  254. gint to_number(const gchar *nstr)
  255. {
  256. register const gchar *p;
  257. if (*nstr == '\0') return -1;
  258. for (p = nstr; *p != '\0'; p++)
  259. if (!g_ascii_isdigit(*p)) return -1;
  260. return atoi(nstr);
  261. }
  262. /* convert integer into string,
  263. nstr must be not lower than 11 characters length */
  264. gchar *itos_buf(gchar *nstr, gint n)
  265. {
  266. g_snprintf(nstr, 11, "%d", n);
  267. return nstr;
  268. }
  269. /* convert integer into string */
  270. gchar *itos(gint n)
  271. {
  272. static gchar nstr[11];
  273. return itos_buf(nstr, n);
  274. }
  275. #define divide(num,divisor,i,d) \
  276. { \
  277. i = num >> divisor; \
  278. d = num & ((1<<divisor)-1); \
  279. d = (d*100) >> divisor; \
  280. }
  281. /*!
  282. * \brief Convert a given size in bytes in a human-readable string
  283. *
  284. * \param size The size expressed in bytes to convert in string
  285. * \return The string that respresents the size in an human-readable way
  286. */
  287. gchar *to_human_readable(goffset size)
  288. {
  289. static gchar str[14];
  290. static gchar *b_format = NULL, *kb_format = NULL,
  291. *mb_format = NULL, *gb_format = NULL;
  292. register int t = 0, r = 0;
  293. if (b_format == NULL) {
  294. b_format = _("%dB");
  295. kb_format = _("%d.%02dKB");
  296. mb_format = _("%d.%02dMB");
  297. gb_format = _("%.2fGB");
  298. }
  299. if (size < (goffset)1024) {
  300. g_snprintf(str, sizeof(str), b_format, (gint)size);
  301. return str;
  302. } else if (size >> 10 < (goffset)1024) {
  303. divide(size, 10, t, r);
  304. g_snprintf(str, sizeof(str), kb_format, t, r);
  305. return str;
  306. } else if (size >> 20 < (goffset)1024) {
  307. divide(size, 20, t, r);
  308. g_snprintf(str, sizeof(str), mb_format, t, r);
  309. return str;
  310. } else {
  311. g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
  312. return str;
  313. }
  314. }
  315. /* strcmp with NULL-checking */
  316. gint strcmp2(const gchar *s1, const gchar *s2)
  317. {
  318. if (s1 == NULL || s2 == NULL)
  319. return -1;
  320. else
  321. return strcmp(s1, s2);
  322. }
  323. /* strstr with NULL-checking */
  324. gchar *strstr2(const gchar *s1, const gchar *s2)
  325. {
  326. if (s1 == NULL || s2 == NULL)
  327. return NULL;
  328. else
  329. return strstr(s1, s2);
  330. }
  331. /* compare paths */
  332. gint path_cmp(const gchar *s1, const gchar *s2)
  333. {
  334. gint len1, len2;
  335. int rc;
  336. #ifdef G_OS_WIN32
  337. gchar *s1buf, *s2buf;
  338. #endif
  339. if (s1 == NULL || s2 == NULL) return -1;
  340. if (*s1 == '\0' || *s2 == '\0') return -1;
  341. #ifdef G_OS_WIN32
  342. s1buf = g_strdup (s1);
  343. s2buf = g_strdup (s2);
  344. subst_char (s1buf, '/', G_DIR_SEPARATOR);
  345. subst_char (s2buf, '/', G_DIR_SEPARATOR);
  346. s1 = s1buf;
  347. s2 = s2buf;
  348. #endif /* !G_OS_WIN32 */
  349. len1 = strlen(s1);
  350. len2 = strlen(s2);
  351. if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
  352. if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
  353. rc = strncmp(s1, s2, MAX(len1, len2));
  354. #ifdef G_OS_WIN32
  355. g_free (s1buf);
  356. g_free (s2buf);
  357. #endif /* !G_OS_WIN32 */
  358. return rc;
  359. }
  360. /* remove trailing return code */
  361. gchar *strretchomp(gchar *str)
  362. {
  363. register gchar *s;
  364. if (!*str) return str;
  365. for (s = str + strlen(str) - 1;
  366. s >= str && (*s == '\n' || *s == '\r');
  367. s--)
  368. *s = '\0';
  369. return str;
  370. }
  371. /* remove trailing character */
  372. gchar *strtailchomp(gchar *str, gchar tail_char)
  373. {
  374. register gchar *s;
  375. if (!*str) return str;
  376. if (tail_char == '\0') return str;
  377. for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
  378. *s = '\0';
  379. return str;
  380. }
  381. /* remove CR (carriage return) */
  382. gchar *strcrchomp(gchar *str)
  383. {
  384. register gchar *s;
  385. if (!*str) return str;
  386. s = str + strlen(str) - 1;
  387. if (*s == '\n' && s > str && *(s - 1) == '\r') {
  388. *(s - 1) = '\n';
  389. *s = '\0';
  390. }
  391. return str;
  392. }
  393. gint file_strip_crs(const gchar *file)
  394. {
  395. FILE *fp = NULL, *outfp = NULL;
  396. gchar buf[4096];
  397. gchar *out = get_tmp_file();
  398. if (file == NULL)
  399. goto freeout;
  400. fp = g_fopen(file, "rb");
  401. if (!fp)
  402. goto freeout;
  403. outfp = g_fopen(out, "wb");
  404. if (!outfp) {
  405. fclose(fp);
  406. goto freeout;
  407. }
  408. while (fgets(buf, sizeof (buf), fp) != NULL) {
  409. strcrchomp(buf);
  410. if (fputs(buf, outfp) == EOF) {
  411. fclose(fp);
  412. fclose(outfp);
  413. goto unlinkout;
  414. }
  415. }
  416. fclose(fp);
  417. if (fclose(outfp) == EOF) {
  418. goto unlinkout;
  419. }
  420. if (move_file(out, file, TRUE) < 0)
  421. goto unlinkout;
  422. g_free(out);
  423. return 0;
  424. unlinkout:
  425. claws_unlink(out);
  426. freeout:
  427. g_free(out);
  428. return -1;
  429. }
  430. /* Similar to `strstr' but this function ignores the case of both strings. */
  431. gchar *strcasestr(const gchar *haystack, const gchar *needle)
  432. {
  433. register size_t haystack_len, needle_len;
  434. haystack_len = strlen(haystack);
  435. needle_len = strlen(needle);
  436. if (haystack_len < needle_len || needle_len == 0)
  437. return NULL;
  438. while (haystack_len >= needle_len) {
  439. if (!g_ascii_strncasecmp(haystack, needle, needle_len))
  440. return (gchar *)haystack;
  441. else {
  442. haystack++;
  443. haystack_len--;
  444. }
  445. }
  446. return NULL;
  447. }
  448. gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
  449. gconstpointer needle, size_t needlelen)
  450. {
  451. const gchar *haystack_ = (const gchar *)haystack;
  452. const gchar *needle_ = (const gchar *)needle;
  453. const gchar *haystack_cur = (const gchar *)haystack;
  454. size_t haystack_left = haystacklen;
  455. if (needlelen == 1)
  456. return memchr(haystack_, *needle_, haystacklen);
  457. while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
  458. != NULL) {
  459. if (haystacklen - (haystack_cur - haystack_) < needlelen)
  460. break;
  461. if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
  462. return (gpointer)haystack_cur;
  463. else{
  464. haystack_cur++;
  465. haystack_left = haystacklen - (haystack_cur - haystack_);
  466. }
  467. }
  468. return NULL;
  469. }
  470. /* Copy no more than N characters of SRC to DEST, with NULL terminating. */
  471. gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
  472. {
  473. register const gchar *s = src;
  474. register gchar *d = dest;
  475. while (--n && *s)
  476. *d++ = *s++;
  477. *d = '\0';
  478. return dest;
  479. }
  480. /* Examine if next block is non-ASCII string */
  481. gboolean is_next_nonascii(const gchar *s)
  482. {
  483. const gchar *p;
  484. /* skip head space */
  485. for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
  486. ;
  487. for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
  488. if (*(guchar *)p > 127 || *(guchar *)p < 32)
  489. return TRUE;
  490. }
  491. return FALSE;
  492. }
  493. gint get_next_word_len(const gchar *s)
  494. {
  495. gint len = 0;
  496. for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
  497. ;
  498. return len;
  499. }
  500. static void trim_subject_for_compare(gchar *str)
  501. {
  502. gchar *srcp;
  503. eliminate_parenthesis(str, '[', ']');
  504. eliminate_parenthesis(str, '(', ')');
  505. g_strstrip(str);
  506. srcp = str + subject_get_prefix_length(str);
  507. if (srcp != str)
  508. memmove(str, srcp, strlen(srcp) + 1);
  509. }
  510. static void trim_subject_for_sort(gchar *str)
  511. {
  512. gchar *srcp;
  513. g_strstrip(str);
  514. srcp = str + subject_get_prefix_length(str);
  515. if (srcp != str)
  516. memmove(str, srcp, strlen(srcp) + 1);
  517. }
  518. /* compare subjects */
  519. gint subject_compare(const gchar *s1, const gchar *s2)
  520. {
  521. gchar *str1, *str2;
  522. if (!s1 || !s2) return -1;
  523. if (!*s1 || !*s2) return -1;
  524. Xstrdup_a(str1, s1, return -1);
  525. Xstrdup_a(str2, s2, return -1);
  526. trim_subject_for_compare(str1);
  527. trim_subject_for_compare(str2);
  528. if (!*str1 || !*str2) return -1;
  529. return strcmp(str1, str2);
  530. }
  531. gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
  532. {
  533. gchar *str1, *str2;
  534. if (!s1 || !s2) return -1;
  535. Xstrdup_a(str1, s1, return -1);
  536. Xstrdup_a(str2, s2, return -1);
  537. trim_subject_for_sort(str1);
  538. trim_subject_for_sort(str2);
  539. return g_utf8_collate(str1, str2);
  540. }
  541. void trim_subject(gchar *str)
  542. {
  543. register gchar *srcp;
  544. gchar op, cl;
  545. gint in_brace;
  546. g_strstrip(str);
  547. srcp = str + subject_get_prefix_length(str);
  548. if (*srcp == '[') {
  549. op = '[';
  550. cl = ']';
  551. } else if (*srcp == '(') {
  552. op = '(';
  553. cl = ')';
  554. } else
  555. op = 0;
  556. if (op) {
  557. ++srcp;
  558. in_brace = 1;
  559. while (*srcp) {
  560. if (*srcp == op)
  561. in_brace++;
  562. else if (*srcp == cl)
  563. in_brace--;
  564. srcp++;
  565. if (in_brace == 0)
  566. break;
  567. }
  568. }
  569. while (g_ascii_isspace(*srcp)) srcp++;
  570. memmove(str, srcp, strlen(srcp) + 1);
  571. }
  572. void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
  573. {
  574. register gchar *srcp, *destp;
  575. gint in_brace;
  576. srcp = destp = str;
  577. while ((destp = strchr(destp, op))) {
  578. in_brace = 1;
  579. srcp = destp + 1;
  580. while (*srcp) {
  581. if (*srcp == op)
  582. in_brace++;
  583. else if (*srcp == cl)
  584. in_brace--;
  585. srcp++;
  586. if (in_brace == 0)
  587. break;
  588. }
  589. while (g_ascii_isspace(*srcp)) srcp++;
  590. memmove(destp, srcp, strlen(srcp) + 1);
  591. }
  592. }
  593. void extract_parenthesis(gchar *str, gchar op, gchar cl)
  594. {
  595. register gchar *srcp, *destp;
  596. gint in_brace;
  597. srcp = destp = str;
  598. while ((srcp = strchr(destp, op))) {
  599. if (destp > str)
  600. *destp++ = ' ';
  601. memmove(destp, srcp + 1, strlen(srcp));
  602. in_brace = 1;
  603. while(*destp) {
  604. if (*destp == op)
  605. in_brace++;
  606. else if (*destp == cl)
  607. in_brace--;
  608. if (in_brace == 0)
  609. break;
  610. destp++;
  611. }
  612. }
  613. *destp = '\0';
  614. }
  615. static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
  616. gchar op, gchar cl)
  617. {
  618. register gchar *srcp, *destp;
  619. gint in_brace;
  620. gboolean in_quote = FALSE;
  621. srcp = destp = str;
  622. while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
  623. if (destp > str)
  624. *destp++ = ' ';
  625. memmove(destp, srcp + 1, strlen(srcp));
  626. in_brace = 1;
  627. while(*destp) {
  628. if (*destp == op && !in_quote)
  629. in_brace++;
  630. else if (*destp == cl && !in_quote)
  631. in_brace--;
  632. else if (*destp == quote_chr)
  633. in_quote ^= TRUE;
  634. if (in_brace == 0)
  635. break;
  636. destp++;
  637. }
  638. }
  639. *destp = '\0';
  640. }
  641. void extract_quote(gchar *str, gchar quote_chr)
  642. {
  643. register gchar *p;
  644. if ((str = strchr(str, quote_chr))) {
  645. p = str;
  646. while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
  647. memmove(p - 1, p, strlen(p) + 1);
  648. p--;
  649. }
  650. if(p) {
  651. *p = '\0';
  652. memmove(str, str + 1, p - str);
  653. }
  654. }
  655. }
  656. void eliminate_address_comment(gchar *str)
  657. {
  658. register gchar *srcp, *destp;
  659. gint in_brace;
  660. srcp = destp = str;
  661. while ((destp = strchr(destp, '"'))) {
  662. if ((srcp = strchr(destp + 1, '"'))) {
  663. srcp++;
  664. if (*srcp == '@') {
  665. destp = srcp + 1;
  666. } else {
  667. while (g_ascii_isspace(*srcp)) srcp++;
  668. memmove(destp, srcp, strlen(srcp) + 1);
  669. }
  670. } else {
  671. *destp = '\0';
  672. break;
  673. }
  674. }
  675. srcp = destp = str;
  676. while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
  677. in_brace = 1;
  678. srcp = destp + 1;
  679. while (*srcp) {
  680. if (*srcp == '(')
  681. in_brace++;
  682. else if (*srcp == ')')
  683. in_brace--;
  684. srcp++;
  685. if (in_brace == 0)
  686. break;
  687. }
  688. while (g_ascii_isspace(*srcp)) srcp++;
  689. memmove(destp, srcp, strlen(srcp) + 1);
  690. }
  691. }
  692. gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
  693. {
  694. gboolean in_quote = FALSE;
  695. while (*str) {
  696. if (*str == c && !in_quote)
  697. return (gchar *)str;
  698. if (*str == quote_chr)
  699. in_quote ^= TRUE;
  700. str++;
  701. }
  702. return NULL;
  703. }
  704. void extract_address(gchar *str)
  705. {
  706. eliminate_address_comment(str);
  707. if (strchr_with_skip_quote(str, '"', '<'))
  708. extract_parenthesis_with_skip_quote(str, '"', '<', '>');
  709. g_strstrip(str);
  710. }
  711. void extract_list_id_str(gchar *str)
  712. {
  713. if (strchr_with_skip_quote(str, '"', '<'))
  714. extract_parenthesis_with_skip_quote(str, '"', '<', '>');
  715. g_strstrip(str);
  716. }
  717. static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
  718. {
  719. gchar *work;
  720. gchar *workp;
  721. if (!str) return addr_list;
  722. Xstrdup_a(work, str, return addr_list);
  723. if (removecomments)
  724. eliminate_address_comment(work);
  725. workp = work;
  726. while (workp && *workp) {
  727. gchar *p, *next;
  728. if ((p = strchr_with_skip_quote(workp, '"', ','))) {
  729. *p = '\0';
  730. next = p + 1;
  731. } else
  732. next = NULL;
  733. if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
  734. extract_parenthesis_with_skip_quote
  735. (workp, '"', '<', '>');
  736. g_strstrip(workp);
  737. if (*workp)
  738. addr_list = g_slist_append(addr_list, g_strdup(workp));
  739. workp = next;
  740. }
  741. return addr_list;
  742. }
  743. GSList *address_list_append(GSList *addr_list, const gchar *str)
  744. {
  745. return address_list_append_real(addr_list, str, TRUE);
  746. }
  747. GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
  748. {
  749. return address_list_append_real(addr_list, str, FALSE);
  750. }
  751. GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
  752. {
  753. const gchar *strp;
  754. if (!str) return msgid_list;
  755. strp = str;
  756. while (strp && *strp) {
  757. const gchar *start, *end;
  758. gchar *msgid;
  759. if ((start = strchr(strp, '<')) != NULL) {
  760. end = strchr(start + 1, '>');
  761. if (!end) break;
  762. } else
  763. break;
  764. msgid = g_strndup(start + 1, end - start - 1);
  765. g_strstrip(msgid);
  766. if (*msgid)
  767. msgid_list = g_slist_prepend(msgid_list, msgid);
  768. else
  769. g_free(msgid);
  770. strp = end + 1;
  771. }
  772. return msgid_list;
  773. }
  774. GSList *references_list_append(GSList *msgid_list, const gchar *str)
  775. {
  776. GSList *list;
  777. list = references_list_prepend(NULL, str);
  778. list = g_slist_reverse(list);
  779. msgid_list = g_slist_concat(msgid_list, list);
  780. return msgid_list;
  781. }
  782. GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
  783. {
  784. gchar *work;
  785. gchar *workp;
  786. if (!str) return group_list;
  787. Xstrdup_a(work, str, return group_list);
  788. workp = work;
  789. while (workp && *workp) {
  790. gchar *p, *next;
  791. if ((p = strchr_with_skip_quote(workp, '"', ','))) {
  792. *p = '\0';
  793. next = p + 1;
  794. } else
  795. next = NULL;
  796. g_strstrip(workp);
  797. if (*workp)
  798. group_list = g_slist_append(group_list,
  799. g_strdup(workp));
  800. workp = next;
  801. }
  802. return group_list;
  803. }
  804. GList *add_history(GList *list, const gchar *str)
  805. {
  806. GList *old;
  807. gchar *oldstr;
  808. cm_return_val_if_fail(str != NULL, list);
  809. old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
  810. if (old) {
  811. oldstr = old->data;
  812. list = g_list_remove(list, old->data);
  813. g_free(oldstr);
  814. } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
  815. GList *last;
  816. last = g_list_last(list);
  817. if (last) {
  818. oldstr = last->data;
  819. list = g_list_remove(list, last->data);
  820. g_free(oldstr);
  821. }
  822. }
  823. list = g_list_prepend(list, g_strdup(str));
  824. return list;
  825. }
  826. void remove_return(gchar *str)
  827. {
  828. register gchar *p = str;
  829. while (*p) {
  830. if (*p == '\n' || *p == '\r')
  831. memmove(p, p + 1, strlen(p));
  832. else
  833. p++;
  834. }
  835. }
  836. void remove_space(gchar *str)
  837. {
  838. register gchar *p = str;
  839. register gint spc;
  840. while (*p) {
  841. spc = 0;
  842. while (g_ascii_isspace(*(p + spc)))
  843. spc++;
  844. if (spc)
  845. memmove(p, p + spc, strlen(p + spc) + 1);
  846. else
  847. p++;
  848. }
  849. }
  850. void unfold_line(gchar *str)
  851. {
  852. register gchar *p = str;
  853. register gint spc;
  854. while (*p) {
  855. if (*p == '\n' || *p == '\r') {
  856. *p++ = ' ';
  857. spc = 0;
  858. while (g_ascii_isspace(*(p + spc)))
  859. spc++;
  860. if (spc)
  861. memmove(p, p + spc, strlen(p + spc) + 1);
  862. } else
  863. p++;
  864. }
  865. }
  866. void subst_char(gchar *str, gchar orig, gchar subst)
  867. {
  868. register gchar *p = str;
  869. while (*p) {
  870. if (*p == orig)
  871. *p = subst;
  872. p++;
  873. }
  874. }
  875. void subst_chars(gchar *str, gchar *orig, gchar subst)
  876. {
  877. register gchar *p = str;
  878. while (*p) {
  879. if (strchr(orig, *p) != NULL)
  880. *p = subst;
  881. p++;
  882. }
  883. }
  884. void subst_for_filename(gchar *str)
  885. {
  886. if (!str)
  887. return;
  888. #ifdef G_OS_WIN32
  889. subst_chars(str, "\t\r\n\\/*:", '_');
  890. #else
  891. subst_chars(str, "\t\r\n\\/*", '_');
  892. #endif
  893. }
  894. void subst_for_shellsafe_filename(gchar *str)
  895. {
  896. if (!str)
  897. return;
  898. subst_for_filename(str);
  899. subst_chars(str, " \"'|&;()<>'!{}[]",'_');
  900. }
  901. gboolean is_ascii_str(const gchar *str)
  902. {
  903. const guchar *p = (const guchar *)str;
  904. while (*p != '\0') {
  905. if (*p != '\t' && *p != ' ' &&
  906. *p != '\r' && *p != '\n' &&
  907. (*p < 32 || *p >= 127))
  908. return FALSE;
  909. p++;
  910. }
  911. return TRUE;
  912. }
  913. static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
  914. {
  915. gchar * position = NULL;
  916. gchar * tmp_pos = NULL;
  917. int i;
  918. if (quote_chars == NULL)
  919. return NULL;
  920. for (i = 0; i < strlen(quote_chars); i++) {
  921. tmp_pos = strrchr (str, quote_chars[i]);
  922. if(position == NULL
  923. || (tmp_pos != NULL && position <= tmp_pos) )
  924. position = tmp_pos;
  925. }
  926. return position;
  927. }
  928. gint get_quote_level(const gchar *str, const gchar *quote_chars)
  929. {
  930. const gchar *first_pos;
  931. const gchar *last_pos;
  932. const gchar *p = str;
  933. gint quote_level = -1;
  934. /* speed up line processing by only searching to the last '>' */
  935. if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
  936. /* skip a line if it contains a '<' before the initial '>' */
  937. if (memchr(str, '<', first_pos - str) != NULL)
  938. return -1;
  939. last_pos = line_has_quote_char_last(first_pos, quote_chars);
  940. } else
  941. return -1;
  942. while (p <= last_pos) {
  943. while (p < last_pos) {
  944. if (g_ascii_isspace(*p))
  945. p++;
  946. else
  947. break;
  948. }
  949. if (strchr(quote_chars, *p))
  950. quote_level++;
  951. else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
  952. /* any characters are allowed except '-','<' and space */
  953. while (*p != '-' && *p != '<'
  954. && !strchr(quote_chars, *p)
  955. && !g_ascii_isspace(*p)
  956. && p < last_pos)
  957. p++;
  958. if (strchr(quote_chars, *p))
  959. quote_level++;
  960. else
  961. break;
  962. }
  963. p++;
  964. }
  965. return quote_level;
  966. }
  967. gint check_line_length(const gchar *str, gint max_chars, gint *line)
  968. {
  969. const gchar *p = str, *q;
  970. gint cur_line = 0, len;
  971. while ((q = strchr(p, '\n')) != NULL) {
  972. len = q - p + 1;
  973. if (len > max_chars) {
  974. if (line)
  975. *line = cur_line;
  976. return -1;
  977. }
  978. p = q + 1;
  979. ++cur_line;
  980. }
  981. len = strlen(p);
  982. if (len > max_chars) {
  983. if (line)
  984. *line = cur_line;
  985. return -1;
  986. }
  987. return 0;
  988. }
  989. const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
  990. {
  991. gchar * position = NULL;
  992. gchar * tmp_pos = NULL;
  993. int i;
  994. if (quote_chars == NULL)
  995. return FALSE;
  996. for (i = 0; i < strlen(quote_chars); i++) {
  997. tmp_pos = strchr (str, quote_chars[i]);
  998. if(position == NULL
  999. || (tmp_pos != NULL && position >= tmp_pos) )
  1000. position = tmp_pos;
  1001. }
  1002. return position;
  1003. }
  1004. static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
  1005. {
  1006. register guint haystack_len, needle_len;
  1007. gboolean in_squote = FALSE, in_dquote = FALSE;
  1008. haystack_len = strlen(haystack);
  1009. needle_len = strlen(needle);
  1010. if (haystack_len < needle_len || needle_len == 0)
  1011. return NULL;
  1012. while (haystack_len >= needle_len) {
  1013. if (!in_squote && !in_dquote &&
  1014. !strncmp(haystack, needle, needle_len))
  1015. return (gchar *)haystack;
  1016. /* 'foo"bar"' -> foo"bar"
  1017. "foo'bar'" -> foo'bar' */
  1018. if (*haystack == '\'') {
  1019. if (in_squote)
  1020. in_squote = FALSE;
  1021. else if (!in_dquote)
  1022. in_squote = TRUE;
  1023. } else if (*haystack == '\"') {
  1024. if (in_dquote)
  1025. in_dquote = FALSE;
  1026. else if (!in_squote)
  1027. in_dquote = TRUE;
  1028. }
  1029. haystack++;
  1030. haystack_len--;
  1031. }
  1032. return NULL;
  1033. }
  1034. gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
  1035. gint max_tokens)
  1036. {
  1037. GSList *string_list = NULL, *slist;
  1038. gchar **str_array, *s, *new_str;
  1039. guint i, n = 1, len;
  1040. cm_return_val_if_fail(str != NULL, NULL);
  1041. cm_return_val_if_fail(delim != NULL, NULL);
  1042. if (max_tokens < 1)
  1043. max_tokens = G_MAXINT;
  1044. s = strstr_with_skip_quote(str, delim);
  1045. if (s) {
  1046. guint delimiter_len = strlen(delim);
  1047. do {
  1048. len = s - str;
  1049. new_str = g_strndup(str, len);
  1050. if (new_str[0] == '\'' || new_str[0] == '\"') {
  1051. if (new_str[len - 1] == new_str[0]) {
  1052. new_str[len - 1] = '\0';
  1053. memmove(new_str, new_str + 1, len - 1);
  1054. }
  1055. }
  1056. string_list = g_slist_prepend(string_list, new_str);
  1057. n++;
  1058. str = s + delimiter_len;
  1059. s = strstr_with_skip_quote(str, delim);
  1060. } while (--max_tokens && s);
  1061. }
  1062. if (*str) {
  1063. new_str = g_strdup(str);
  1064. if (new_str[0] == '\'' || new_str[0] == '\"') {
  1065. len = strlen(str);
  1066. if (new_str[len - 1] == new_str[0]) {
  1067. new_str[len - 1] = '\0';
  1068. memmove(new_str, new_str + 1, len - 1);
  1069. }
  1070. }
  1071. string_list = g_slist_prepend(string_list, new_str);
  1072. n++;
  1073. }
  1074. str_array = g_new(gchar*, n);
  1075. i = n - 1;
  1076. str_array[i--] = NULL;
  1077. for (slist = string_list; slist; slist = slist->next)
  1078. str_array[i--] = slist->data;
  1079. g_slist_free(string_list);
  1080. return str_array;
  1081. }
  1082. gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
  1083. {
  1084. gchar *abbrev_group;
  1085. gchar *ap;
  1086. const gchar *p = group;
  1087. const gchar *last;
  1088. cm_return_val_if_fail(group != NULL, NULL);
  1089. last = group + strlen(group);
  1090. abbrev_group = ap = g_malloc(strlen(group) + 1);
  1091. while (*p) {
  1092. while (*p == '.')
  1093. *ap++ = *p++;
  1094. if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
  1095. *ap++ = *p++;
  1096. while (*p != '.') p++;
  1097. } else {
  1098. strcpy(ap, p);
  1099. return abbrev_group;
  1100. }
  1101. }
  1102. *ap = '\0';
  1103. return abbrev_group;
  1104. }
  1105. gchar *trim_string(const gchar *str, gint len)
  1106. {
  1107. const gchar *p = str;
  1108. gint mb_len;
  1109. gchar *new_str;
  1110. gint new_len = 0;
  1111. if (!str) return NULL;
  1112. if (strlen(str) <= len)
  1113. return g_strdup(str);
  1114. if (g_utf8_validate(str, -1, NULL) == FALSE)
  1115. return g_strdup(str);
  1116. while (*p != '\0') {
  1117. mb_len = g_utf8_skip[*(guchar *)p];
  1118. if (mb_len == 0)
  1119. break;
  1120. else if (new_len + mb_len > len)
  1121. break;
  1122. new_len += mb_len;
  1123. p += mb_len;
  1124. }
  1125. Xstrndup_a(new_str, str, new_len, return g_strdup(str));
  1126. return g_strconcat(new_str, "...", NULL);
  1127. }
  1128. GList *uri_list_extract_filenames(const gchar *uri_list)
  1129. {
  1130. GList *result = NULL;
  1131. const gchar *p, *q;
  1132. gchar *escaped_utf8uri;
  1133. p = uri_list;
  1134. while (p) {
  1135. if (*p != '#') {
  1136. while (g_ascii_isspace(*p)) p++;
  1137. if (!strncmp(p, "file:", 5)) {
  1138. q = p;
  1139. q += 5;
  1140. while (*q && *q != '\n' && *q != '\r') q++;
  1141. if (q > p) {
  1142. gchar *file, *locale_file = NULL;
  1143. q--;
  1144. while (q > p && g_ascii_isspace(*q))
  1145. q--;
  1146. Xalloca(escaped_utf8uri, q - p + 2,
  1147. return result);
  1148. Xalloca(file, q - p + 2,
  1149. return result);
  1150. *file = '\0';
  1151. strncpy(escaped_utf8uri, p, q - p + 1);
  1152. escaped_utf8uri[q - p + 1] = '\0';
  1153. decode_uri(file, escaped_utf8uri);
  1154. /*
  1155. * g_filename_from_uri() rejects escaped/locale encoded uri
  1156. * string which come from Nautilus.
  1157. */
  1158. #ifndef G_OS_WIN32
  1159. if (g_utf8_validate(file, -1, NULL))
  1160. locale_file
  1161. = conv_codeset_strdup(
  1162. file + 5,
  1163. CS_UTF_8,
  1164. conv_get_locale_charset_str());
  1165. if (!locale_file)
  1166. locale_file = g_strdup(file + 5);
  1167. #else
  1168. locale_file = g_filename_from_uri(file, NULL, NULL);
  1169. #endif
  1170. result = g_list_append(result, locale_file);
  1171. }
  1172. }
  1173. }
  1174. p = strchr(p, '\n');
  1175. if (p) p++;
  1176. }
  1177. return result;
  1178. }
  1179. /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
  1180. * characters
  1181. */
  1182. static gint axtoi(const gchar *hexstr)
  1183. {
  1184. gint hi, lo, result;
  1185. hi = hexstr[0];
  1186. if ('0' <= hi && hi <= '9') {
  1187. hi -= '0';
  1188. } else
  1189. if ('a' <= hi && hi <= 'f') {
  1190. hi -= ('a' - 10);
  1191. } else
  1192. if ('A' <= hi && hi <= 'F') {
  1193. hi -= ('A' - 10);
  1194. }
  1195. lo = hexstr[1];
  1196. if ('0' <= lo && lo <= '9') {
  1197. lo -= '0';
  1198. } else
  1199. if ('a' <= lo && lo <= 'f') {
  1200. lo -= ('a'-10);
  1201. } else
  1202. if ('A' <= lo && lo <= 'F') {
  1203. lo -= ('A' - 10);
  1204. }
  1205. result = lo + (16 * hi);
  1206. return result;
  1207. }
  1208. gboolean is_uri_string(const gchar *str)
  1209. {
  1210. while (str && *str && g_ascii_isspace(*str))
  1211. str++;
  1212. return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
  1213. g_ascii_strncasecmp(str, "https://", 8) == 0 ||
  1214. g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
  1215. g_ascii_strncasecmp(str, "www.", 4) == 0);
  1216. }
  1217. gchar *get_uri_path(const gchar *uri)
  1218. {
  1219. while (uri && *uri && g_ascii_isspace(*uri))
  1220. uri++;
  1221. if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
  1222. return (gchar *)(uri + 7);
  1223. else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
  1224. return (gchar *)(uri + 8);
  1225. else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
  1226. return (gchar *)(uri + 6);
  1227. else
  1228. return (gchar *)uri;
  1229. }
  1230. gint get_uri_len(const gchar *str)
  1231. {
  1232. const gchar *p;
  1233. if (is_uri_string(str)) {
  1234. for (p = str; *p != '\0'; p++) {
  1235. if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
  1236. break;
  1237. }
  1238. return p - str;
  1239. }
  1240. return 0;
  1241. }
  1242. /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
  1243. * plusses, and escape characters are used)
  1244. */
  1245. void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
  1246. {
  1247. gchar *dec = decoded_uri;
  1248. const gchar *enc = encoded_uri;
  1249. while (*enc) {
  1250. if (*enc == '%') {
  1251. enc++;
  1252. if (isxdigit((guchar)enc[0]) &&
  1253. isxdigit((guchar)enc[1])) {
  1254. *dec = axtoi(enc);
  1255. dec++;
  1256. enc += 2;
  1257. }
  1258. } else {
  1259. if (with_plus && *enc == '+')
  1260. *dec = ' ';
  1261. else
  1262. *dec = *enc;
  1263. dec++;
  1264. enc++;
  1265. }
  1266. }
  1267. *dec = '\0';
  1268. }
  1269. void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
  1270. {
  1271. decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
  1272. }
  1273. static gchar *decode_uri_gdup(const gchar *encoded_uri)
  1274. {
  1275. gchar *buffer = g_malloc(strlen(encoded_uri)+1);
  1276. decode_uri_with_plus(buffer, encoded_uri, FALSE);
  1277. return buffer;
  1278. }
  1279. gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
  1280. gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
  1281. {
  1282. gchar *tmp_mailto;
  1283. gchar *p;
  1284. const gchar *forbidden_uris[] = { ".gnupg/",
  1285. "/etc/passwd",
  1286. "/etc/shadow",
  1287. ".ssh/",
  1288. "../",
  1289. NULL };
  1290. gint num_attach = 0;
  1291. gchar **my_att = NULL;
  1292. Xstrdup_a(tmp_mailto, mailto, return -1);
  1293. if (!strncmp(tmp_mailto, "mailto:", 7))
  1294. tmp_mailto += 7;
  1295. p = strchr(tmp_mailto, '?');
  1296. if (p) {
  1297. *p = '\0';
  1298. p++;
  1299. }
  1300. if (to && !*to)
  1301. *to = decode_uri_gdup(tmp_mailto);
  1302. my_att = g_malloc(sizeof(char *));
  1303. my_att[0] = NULL;
  1304. while (p) {
  1305. gchar *field, *value;
  1306. field = p;
  1307. p = strchr(p, '=');
  1308. if (!p) break;
  1309. *p = '\0';
  1310. p++;
  1311. value = p;
  1312. p = strchr(p, '&');
  1313. if (p) {
  1314. *p = '\0';
  1315. p++;
  1316. }
  1317. if (*value == '\0') continue;
  1318. if (from && !g_ascii_strcasecmp(field, "from")) {
  1319. if (!*from) {
  1320. *from = decode_uri_gdup(value);
  1321. } else {
  1322. gchar *tmp = decode_uri_gdup(value);
  1323. gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
  1324. g_free(*from);
  1325. *from = new_from;
  1326. }
  1327. } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
  1328. if (!*cc) {
  1329. *cc = decode_uri_gdup(value);
  1330. } else {
  1331. gchar *tmp = decode_uri_gdup(value);
  1332. gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
  1333. g_free(*cc);
  1334. *cc = new_cc;
  1335. }
  1336. } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
  1337. if (!*bcc) {
  1338. *bcc = decode_uri_gdup(value);
  1339. } else {
  1340. gchar *tmp = decode_uri_gdup(value);
  1341. gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
  1342. g_free(*bcc);
  1343. *bcc = new_bcc;
  1344. }
  1345. } else if (subject && !*subject &&
  1346. !g_ascii_strcasecmp(field, "subject")) {
  1347. *subject = decode_uri_gdup(value);
  1348. } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
  1349. *body = decode_uri_gdup(value);
  1350. } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
  1351. gchar *tmp = decode_uri_gdup(value);
  1352. if (!g_file_get_contents(tmp, body, NULL, NULL)) {
  1353. g_warning("Error: couldn't set insert file '%s' in body\n", value);
  1354. }
  1355. g_free(tmp);
  1356. tmp = NULL;
  1357. } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
  1358. int i = 0;
  1359. gchar *tmp = decode_uri_gdup(value);
  1360. for (; forbidden_uris[i]; i++) {
  1361. if (strstr(tmp, forbidden_uris[i])) {
  1362. g_print("Refusing to attach '%s', potential private data leak\n",
  1363. tmp);
  1364. g_free(tmp);
  1365. tmp = NULL;
  1366. break;
  1367. }
  1368. }
  1369. if (tmp) {
  1370. /* attach is correct */
  1371. num_attach++;
  1372. my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
  1373. my_att[num_attach-1] = tmp;
  1374. my_att[num_attach] = NULL;
  1375. }
  1376. } else if (inreplyto && !*inreplyto &&
  1377. !g_ascii_strcasecmp(field, "in-reply-to")) {
  1378. *inreplyto = decode_uri_gdup(value);
  1379. }
  1380. }
  1381. if (attach)
  1382. *attach = my_att;
  1383. return 0;
  1384. }
  1385. #ifdef G_OS_WIN32
  1386. #include <windows.h>
  1387. #ifndef CSIDL_APPDATA
  1388. #define CSIDL_APPDATA 0x001a
  1389. #endif
  1390. #ifndef CSIDL_LOCAL_APPDATA
  1391. #define CSIDL_LOCAL_APPDATA 0x001c
  1392. #endif
  1393. #ifndef CSIDL_FLAG_CREATE
  1394. #define CSIDL_FLAG_CREATE 0x8000
  1395. #endif
  1396. #define DIM(v) (sizeof(v)/sizeof((v)[0]))
  1397. #define RTLD_LAZY 0
  1398. const char *
  1399. w32_strerror (int w32_errno)
  1400. {
  1401. static char strerr[256];
  1402. int ec = (int)GetLastError ();
  1403. if (w32_errno == 0)
  1404. w32_errno = ec;
  1405. FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
  1406. MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  1407. strerr, DIM (strerr)-1, NULL);
  1408. return strerr;
  1409. }
  1410. static __inline__ void *
  1411. dlopen (const char * name, int flag)
  1412. {
  1413. void * hd = LoadLibrary (name);
  1414. return hd;
  1415. }
  1416. static __inline__ void *
  1417. dlsym (void * hd, const char * sym)
  1418. {
  1419. if (hd && sym)
  1420. {
  1421. void * fnc = GetProcAddress (hd, sym);
  1422. if (!fnc)
  1423. return NULL;
  1424. return fnc;
  1425. }
  1426. return NULL;
  1427. }
  1428. static __inline__ const char *
  1429. dlerror (void)
  1430. {
  1431. return w32_strerror (0);
  1432. }
  1433. static __inline__ int
  1434. dlclose (void * hd)
  1435. {
  1436. if (hd)
  1437. {
  1438. FreeLibrary (hd);
  1439. return 0;
  1440. }
  1441. return -1;
  1442. }
  1443. static HRESULT
  1444. w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
  1445. {
  1446. static int initialized;
  1447. static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
  1448. if (!initialized)
  1449. {
  1450. static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
  1451. void *handle;
  1452. int i;
  1453. initialized = 1;
  1454. for (i=0, handle = NULL; !handle && dllnames[i]; i++)
  1455. {
  1456. handle = dlopen (dllnames[i], RTLD_LAZY);
  1457. if (handle)
  1458. {
  1459. func = dlsym (handle, "SHGetFolderPathW");
  1460. if (!func)
  1461. {
  1462. dlclose (handle);
  1463. handle = NULL;
  1464. }
  1465. }
  1466. }
  1467. }
  1468. if (func)
  1469. return func (a,b,c,d,e);
  1470. else
  1471. return -1;
  1472. }
  1473. /* Returns a static string with the directroy from which the module
  1474. has been loaded. Returns an empty string on error. */
  1475. static char *w32_get_module_dir(void)
  1476. {
  1477. static char *moddir;
  1478. if (!moddir) {
  1479. char name[MAX_PATH+10];
  1480. char *p;
  1481. if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
  1482. *name = 0;
  1483. else {
  1484. p = strrchr (name, '\\');
  1485. if (p)
  1486. *p = 0;
  1487. else
  1488. *name = 0;
  1489. }
  1490. moddir = g_strdup (name);
  1491. }
  1492. return moddir;
  1493. }
  1494. #endif /* G_OS_WIN32 */
  1495. /* Return a static string with the locale dir. */
  1496. const gchar *get_locale_dir(void)
  1497. {
  1498. static gchar *loc_dir;
  1499. #ifdef G_OS_WIN32
  1500. if (!loc_dir)
  1501. loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
  1502. "\\share\\locale", NULL);
  1503. #endif
  1504. if (!loc_dir)
  1505. loc_dir = LOCALEDIR;
  1506. return loc_dir;
  1507. }
  1508. const gchar *get_home_dir(void)
  1509. {
  1510. #ifdef G_OS_WIN32
  1511. static char home_dir_utf16[MAX_PATH] = "";
  1512. static gchar *home_dir_utf8 = NULL;
  1513. if (home_dir_utf16[0] == '\0') {
  1514. if (w32_shgetfolderpath
  1515. (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
  1516. NULL, 0, home_dir_utf16) < 0)
  1517. strcpy (home_dir_utf16, "C:\\Sylpheed");
  1518. home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
  1519. }
  1520. return home_dir_utf8;
  1521. #else
  1522. static const gchar *homeenv = NULL;
  1523. if (homeenv)
  1524. return homeenv;
  1525. if (!homeenv && g_getenv("HOME") != NULL)
  1526. homeenv = g_strdup(g_getenv("HOME"));
  1527. if (!homeenv)
  1528. homeenv = g_get_home_dir();
  1529. return homeenv;
  1530. #endif
  1531. }
  1532. static gchar *claws_rc_dir = NULL;
  1533. static gboolean rc_dir_alt = FALSE;
  1534. const gchar *get_rc_dir(void)
  1535. {
  1536. if (!claws_rc_dir)
  1537. claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
  1538. RC_DIR, NULL);
  1539. return claws_rc_dir;
  1540. }
  1541. void set_rc_dir(const gchar *dir)
  1542. {
  1543. if (claws_rc_dir != NULL) {
  1544. g_print("Error: rc_dir already set\n");
  1545. } else {
  1546. rc_dir_alt = TRUE;
  1547. if (g_path_is_absolute(dir))
  1548. claws_rc_dir = g_strdup(dir);
  1549. else {
  1550. claws_rc_dir = g_strconcat(g_get_current_dir(),
  1551. G_DIR_SEPARATOR_S, dir, NULL);
  1552. }
  1553. debug_print("set rc_dir to %s\n", claws_rc_dir);
  1554. if (!is_dir_exist(claws_rc_dir)) {
  1555. if (make_dir_hier(claws_rc_dir) != 0) {
  1556. g_print("Error: can't create %s\n",
  1557. claws_rc_dir);
  1558. }
  1559. }
  1560. }
  1561. }
  1562. gboolean rc_dir_is_alt(void) {
  1563. return rc_dir_alt;
  1564. }
  1565. const gchar *get_mail_base_dir(void)
  1566. {
  1567. return get_home_dir();
  1568. }
  1569. #ifdef MAEMO
  1570. const gchar *prefs_common_get_data_root(void);
  1571. gchar *last_data_root = NULL;
  1572. #endif
  1573. const gchar *get_news_cache_dir(void)
  1574. {
  1575. static gchar *news_cache_dir = NULL;
  1576. #ifdef MAEMO
  1577. const gchar *data_root = prefs_common_get_data_root();
  1578. if (strcmp2(data_root, last_data_root)) {
  1579. g_free(news_cache_dir);
  1580. news_cache_dir = NULL;
  1581. }
  1582. #endif
  1583. if (!news_cache_dir)
  1584. #ifndef MAEMO
  1585. news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1586. NEWS_CACHE_DIR, NULL);
  1587. #else
  1588. {
  1589. if (data_root) {
  1590. news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
  1591. "Claws", G_DIR_SEPARATOR_S,
  1592. g_get_user_name(), G_DIR_SEPARATOR_S,
  1593. NEWS_CACHE_DIR, NULL);
  1594. g_free(last_data_root);
  1595. last_data_root = g_strdup(last_data_root);
  1596. } else {
  1597. news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1598. NEWS_CACHE_DIR, NULL);
  1599. g_free(last_data_root);
  1600. last_data_root = NULL;
  1601. }
  1602. }
  1603. #endif
  1604. return news_cache_dir;
  1605. }
  1606. const gchar *get_imap_cache_dir(void)
  1607. {
  1608. static gchar *imap_cache_dir = NULL;
  1609. #ifdef MAEMO
  1610. const gchar *data_root = prefs_common_get_data_root();
  1611. if (strcmp2(data_root, last_data_root)) {
  1612. g_free(imap_cache_dir);
  1613. imap_cache_dir = NULL;
  1614. }
  1615. #endif
  1616. if (!imap_cache_dir)
  1617. #ifndef MAEMO
  1618. imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1619. IMAP_CACHE_DIR, NULL);
  1620. #else
  1621. {
  1622. if (data_root) {
  1623. imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
  1624. "Claws", G_DIR_SEPARATOR_S,
  1625. g_get_user_name(), G_DIR_SEPARATOR_S,
  1626. IMAP_CACHE_DIR, NULL);
  1627. g_free(last_data_root);
  1628. last_data_root = g_strdup(last_data_root);
  1629. } else {
  1630. imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1631. IMAP_CACHE_DIR, NULL);
  1632. g_free(last_data_root);
  1633. last_data_root = NULL;
  1634. }
  1635. }
  1636. #endif
  1637. return imap_cache_dir;
  1638. }
  1639. const gchar *get_mime_tmp_dir(void)
  1640. {
  1641. static gchar *mime_tmp_dir = NULL;
  1642. if (!mime_tmp_dir)
  1643. mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1644. MIME_TMP_DIR, NULL);
  1645. return mime_tmp_dir;
  1646. }
  1647. const gchar *get_template_dir(void)
  1648. {
  1649. static gchar *template_dir = NULL;
  1650. if (!template_dir)
  1651. template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1652. TEMPLATE_DIR, NULL);
  1653. return template_dir;
  1654. }
  1655. #ifdef G_OS_WIN32
  1656. const gchar *get_cert_file(void)
  1657. {
  1658. const gchar *cert_file = NULL;
  1659. if (!cert_file)
  1660. cert_file = g_strconcat(w32_get_module_dir(),
  1661. "\\share\\claws-mail\\",
  1662. "ca-certificates.crt",
  1663. NULL);
  1664. return cert_file;
  1665. }
  1666. #endif
  1667. /* Return the filepath of the claws-mail.desktop file */
  1668. const gchar *get_desktop_file(void)
  1669. {
  1670. #ifdef DESKTOPFILEPATH
  1671. return DESKTOPFILEPATH;
  1672. #else
  1673. return NULL;
  1674. #endif
  1675. }
  1676. /* Return the default directory for Plugins. */
  1677. const gchar *get_plugin_dir(void)
  1678. {
  1679. #ifdef G_OS_WIN32
  1680. static gchar *plugin_dir = NULL;
  1681. if (!plugin_dir)
  1682. plugin_dir = g_strconcat(w32_get_module_dir(),
  1683. "\\lib\\claws-mail\\plugins\\",
  1684. NULL);
  1685. return plugin_dir;
  1686. #else
  1687. if (is_dir_exist(PLUGINDIR))
  1688. return PLUGINDIR;
  1689. else {
  1690. static gchar *plugin_dir = NULL;
  1691. if (!plugin_dir)
  1692. plugin_dir = g_strconcat(get_rc_dir(),
  1693. G_DIR_SEPARATOR_S, "plugins",
  1694. G_DIR_SEPARATOR_S, NULL);
  1695. return plugin_dir;
  1696. }
  1697. #endif
  1698. }
  1699. #ifdef G_OS_WIN32
  1700. /* Return the default directory for Themes. */
  1701. const gchar *get_themes_dir(void)
  1702. {
  1703. static gchar *themes_dir = NULL;
  1704. if (!themes_dir)
  1705. themes_dir = g_strconcat(w32_get_module_dir(),
  1706. "\\share\\claws-mail\\themes",
  1707. NULL);
  1708. return themes_dir;
  1709. }
  1710. #endif
  1711. const gchar *get_tmp_dir(void)
  1712. {
  1713. static gchar *tmp_dir = NULL;
  1714. if (!tmp_dir)
  1715. tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
  1716. TMP_DIR, NULL);
  1717. return tmp_dir;
  1718. }
  1719. gchar *get_tmp_file(void)
  1720. {
  1721. gchar *tmp_file;
  1722. static guint32 id = 0;
  1723. tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
  1724. get_tmp_dir(), G_DIR_SEPARATOR, id++);
  1725. return tmp_file;
  1726. }
  1727. const gchar *get_domain_name(void)
  1728. {
  1729. #ifdef G_OS_UNIX
  1730. static gchar *domain_name = NULL;
  1731. if (!domain_name) {
  1732. struct hostent *hp;
  1733. char hostname[256];
  1734. if (gethostname(hostname, sizeof(hostname)) != 0) {
  1735. perror("gethostname");
  1736. domain_name = "unknown";
  1737. } else {
  1738. hostname[sizeof(hostname) - 1] = '\0';
  1739. if ((hp = my_gethostbyname(hostname)) == NULL) {
  1740. perror("gethostbyname");
  1741. domain_name = g_strdup(hostname);
  1742. } else {
  1743. domain_name = g_strdup(hp->h_name);
  1744. }
  1745. }
  1746. debug_print("domain name = %s\n", domain_name);
  1747. }
  1748. return domain_name;
  1749. #else
  1750. return "unknown";
  1751. #endif
  1752. }
  1753. off_t get_file_size(const gchar *file)
  1754. {
  1755. struct stat s;
  1756. if (g_stat(file, &s) < 0) {
  1757. FILE_OP_ERROR(file, "stat");
  1758. return -1;
  1759. }
  1760. return s.st_size;
  1761. }
  1762. time_t get_file_mtime(const gchar *file)
  1763. {
  1764. struct stat s;
  1765. if (g_stat(file, &s) < 0) {
  1766. FILE_OP_ERROR(file, "stat");
  1767. return -1;
  1768. }
  1769. return s.st_mtime;
  1770. }
  1771. off_t get_file_size_as_crlf(const gchar *file)
  1772. {
  1773. FILE *fp;
  1774. off_t size = 0;
  1775. gchar buf[BUFFSIZE];
  1776. if ((fp = g_fopen(file, "rb")) == NULL) {
  1777. FILE_OP_ERROR(file, "g_fopen");
  1778. return -1;
  1779. }
  1780. while (fgets(buf, sizeof(buf), fp) != NULL) {
  1781. strretchomp(buf);
  1782. size += strlen(buf) + 2;
  1783. }
  1784. if (ferror(fp)) {
  1785. FILE_OP_ERROR(file, "fgets");
  1786. size = -1;
  1787. }
  1788. fclose(fp);
  1789. return size;
  1790. }
  1791. gboolean file_exist(const gchar *file, gboolean allow_fifo)
  1792. {
  1793. struct stat s;
  1794. if (file == NULL)
  1795. return FALSE;
  1796. if (g_stat(file, &s) < 0) {
  1797. if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
  1798. return FALSE;
  1799. }
  1800. if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
  1801. return TRUE;
  1802. return FALSE;
  1803. }
  1804. /* Test on whether FILE is a relative file name. This is
  1805. * straightforward for Unix but more complex for Windows. */
  1806. gboolean is_relative_filename(const gchar *file)
  1807. {
  1808. if (!file)
  1809. return TRUE;
  1810. #ifdef G_OS_WIN32
  1811. if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
  1812. return FALSE; /* Prefixed with a hostname - this can't
  1813. * be a relative name. */
  1814. if ( ((*file >= 'a' && *file <= 'z')
  1815. || (*file >= 'A' && *file <= 'Z'))
  1816. && file[1] == ':')
  1817. file += 2; /* Skip drive letter. */
  1818. return !(*file == '\\' || *file == '/');
  1819. #else
  1820. return !(*file == G_DIR_SEPARATOR);
  1821. #endif
  1822. }
  1823. gboolean is_dir_exist(const gchar *dir)
  1824. {
  1825. if (dir == NULL)
  1826. return FALSE;
  1827. return g_file_test(dir, G_FILE_TEST_IS_DIR);
  1828. }
  1829. gboolean is_file_entry_exist(const gchar *file)
  1830. {
  1831. if (file == NULL)
  1832. return FALSE;
  1833. return g_file_test(file, G_FILE_TEST_EXISTS);
  1834. }
  1835. gboolean dirent_is_regular_file(struct dirent *d)
  1836. {
  1837. #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
  1838. if (d->d_type == DT_REG)
  1839. return TRUE;
  1840. else if (d->d_type != DT_UNKNOWN)
  1841. return FALSE;
  1842. #endif
  1843. return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
  1844. }
  1845. gint change_dir(const gchar *dir)
  1846. {
  1847. gchar *prevdir = NULL;
  1848. if (debug_mode)
  1849. prevdir = g_get_current_dir();
  1850. if (g_chdir(dir) < 0) {
  1851. FILE_OP_ERROR(dir, "chdir");
  1852. if (debug_mode) g_free(prevdir);
  1853. return -1;
  1854. } else if (debug_mode) {
  1855. gchar *cwd;
  1856. cwd = g_get_current_dir();
  1857. if (strcmp(prevdir, cwd) != 0)
  1858. g_print("current dir: %s\n", cwd);
  1859. g_free(cwd);
  1860. g_free(prevdir);
  1861. }
  1862. return 0;
  1863. }
  1864. gint make_dir(const gchar *dir)
  1865. {
  1866. if (g_mkdir(dir, S_IRWXU) < 0) {
  1867. FILE_OP_ERROR(dir, "mkdir");
  1868. return -1;
  1869. }
  1870. if (g_chmod(dir, S_IRWXU) < 0)
  1871. FILE_OP_ERROR(dir, "chmod");
  1872. return 0;
  1873. }
  1874. gint make_dir_hier(const gchar *dir)
  1875. {
  1876. gchar *parent_dir;
  1877. const gchar *p;
  1878. for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
  1879. parent_dir = g_strndup(dir, p - dir);
  1880. if (*parent_dir != '\0') {
  1881. if (!is_dir_exist(parent_dir)) {
  1882. if (make_dir(parent_dir) < 0) {
  1883. g_free(parent_dir);
  1884. return -1;
  1885. }
  1886. }
  1887. }
  1888. g_free(parent_dir);
  1889. }
  1890. if (!is_dir_exist(dir)) {
  1891. if (make_dir(dir) < 0)
  1892. return -1;
  1893. }
  1894. return 0;
  1895. }
  1896. gint remove_all_files(const gchar *dir)
  1897. {
  1898. GDir *dp;
  1899. const gchar *dir_name;
  1900. gchar *prev_dir;
  1901. prev_dir = g_get_current_dir();
  1902. if (g_chdir(dir) < 0) {
  1903. FILE_OP_ERROR(dir, "chdir");
  1904. g_free(prev_dir);
  1905. return -1;
  1906. }
  1907. if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
  1908. g_warning("failed to open directory: %s\n", dir);
  1909. g_free(prev_dir);
  1910. return -1;
  1911. }
  1912. while ((dir_name = g_dir_read_name(dp)) != NULL) {
  1913. if (claws_unlink(dir_name) < 0)
  1914. FILE_OP_ERROR(dir_name, "unlink");
  1915. }
  1916. g_dir_close(dp);
  1917. if (g_chdir(prev_dir) < 0) {
  1918. FILE_OP_ERROR(prev_dir, "chdir");
  1919. g_free(prev_dir);
  1920. return -1;
  1921. }
  1922. g_free(prev_dir);
  1923. return 0;
  1924. }
  1925. gint remove_numbered_files(const gchar *dir, guint first, guint last)
  1926. {
  1927. GDir *dp;
  1928. const gchar *dir_name;
  1929. gchar *prev_dir;
  1930. gint file_no;
  1931. if (first == last) {
  1932. /* Skip all the dir reading part. */
  1933. gchar *filename = g_strdup_printf("%s%s%u", dir, G_DIR_SEPARATOR_S, first);
  1934. if (claws_unlink(filename) < 0) {
  1935. FILE_OP_ERROR(filename, "unlink");
  1936. g_free(filename);
  1937. return -1;
  1938. }
  1939. g_free(filename);
  1940. return 0;
  1941. }
  1942. prev_dir = g_get_current_dir();
  1943. if (g_chdir(dir) < 0) {
  1944. FILE_OP_ERROR(dir, "chdir");
  1945. g_free(prev_dir);
  1946. return -1;
  1947. }
  1948. if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
  1949. g_warning("failed to open directory: %s\n", dir);
  1950. g_free(prev_dir);
  1951. return -1;
  1952. }
  1953. while ((dir_name = g_dir_read_name(dp)) != NULL) {
  1954. file_no = to_number(dir_name);
  1955. if (file_no > 0 && first <= file_no && file_no <= last) {
  1956. if (is_dir_exist(dir_name))
  1957. continue;
  1958. if (claws_unlink(dir_name) < 0)
  1959. FILE_OP_ERROR(dir_name, "unlink");
  1960. }
  1961. }
  1962. g_dir_close(dp);
  1963. if (g_chdir(prev_dir) < 0) {
  1964. FILE_OP_ERROR(prev_dir, "chdir");
  1965. g_free(prev_dir);
  1966. return -1;
  1967. }
  1968. g_free(prev_dir);
  1969. return 0;
  1970. }
  1971. gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
  1972. {
  1973. GDir *dp;
  1974. const gchar *dir_name;
  1975. gchar *prev_dir;
  1976. gint file_no;
  1977. prev_dir = g_get_current_dir();
  1978. if (g_chdir(dir) < 0) {
  1979. FILE_OP_ERROR(dir, "chdir");
  1980. g_free(prev_dir);
  1981. return -1;
  1982. }
  1983. if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
  1984. FILE_OP_ERROR(dir, "opendir");
  1985. g_free(prev_dir);
  1986. return -1;
  1987. }
  1988. while ((dir_name = g_dir_read_name(dp)) != NULL) {
  1989. file_no = to_number(dir_name);
  1990. if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
  1991. debug_print("removing unwanted file %d from %s\n", file_no, dir);
  1992. if (is_dir_exist(dir_name))
  1993. continue;
  1994. if (claws_unlink(dir_name) < 0)
  1995. FILE_OP_ERROR(dir_name, "unlink");
  1996. }
  1997. }
  1998. g_dir_close(dp);
  1999. if (g_chdir(prev_dir) < 0) {
  2000. FILE_OP_ERROR(prev_dir, "chdir");
  2001. g_free(prev_dir);
  2002. return -1;
  2003. }
  2004. g_free(prev_dir);
  2005. return 0;
  2006. }
  2007. gint remove_all_numbered_files(const gchar *dir)
  2008. {
  2009. return remove_numbered_files(dir, 0, UINT_MAX);
  2010. }
  2011. gint remove_dir_recursive(const gchar *dir)
  2012. {
  2013. struct stat s;
  2014. GDir *dp;
  2015. const gchar *dir_name;
  2016. gchar *prev_dir;
  2017. if (g_stat(dir, &s) < 0) {
  2018. FILE_OP_ERROR(dir, "stat");
  2019. if (ENOENT == errno) return 0;
  2020. return -1;
  2021. }
  2022. if (!S_ISDIR(s.st_mode)) {
  2023. if (claws_unlink(dir) < 0) {
  2024. FILE_OP_ERROR(dir, "unlink");
  2025. return -1;
  2026. }
  2027. return 0;
  2028. }
  2029. prev_dir = g_get_current_dir();
  2030. /* g_print("prev_dir = %s\n", prev_dir); */
  2031. if (!path_cmp(prev_dir, dir)) {
  2032. g_free(prev_dir);
  2033. if (g_chdir("..") < 0) {
  2034. FILE_OP_ERROR(dir, "chdir");
  2035. return -1;
  2036. }
  2037. prev_dir = g_get_current_dir();
  2038. }
  2039. if (g_chdir(dir) < 0) {
  2040. FILE_OP_ERROR(dir, "chdir");
  2041. g_free(prev_dir);
  2042. return -1;
  2043. }
  2044. if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
  2045. g_warning("failed to open directory: %s\n", dir);
  2046. g_chdir(prev_dir);
  2047. g_free(prev_dir);
  2048. return -1;
  2049. }
  2050. /* remove all files in the directory */
  2051. while ((dir_name = g_dir_read_name(dp)) != NULL) {
  2052. /* g_print("removing %s\n", dir_name); */
  2053. if (is_dir_exist(dir_name)) {
  2054. if (remove_dir_recursive(dir_name) < 0) {
  2055. g_warning("can't remove directory\n");
  2056. return -1;
  2057. }
  2058. } else {
  2059. if (claws_unlink(dir_name) < 0)
  2060. FILE_OP_ERROR(dir_name, "unlink");
  2061. }
  2062. }
  2063. g_dir_close(dp);
  2064. if (g_chdir(prev_dir) < 0) {
  2065. FILE_OP_ERROR(prev_dir, "chdir");
  2066. g_free(prev_dir);
  2067. return -1;
  2068. }
  2069. g_free(prev_dir);
  2070. if (g_rmdir(dir) < 0) {
  2071. FILE_OP_ERROR(dir, "rmdir");
  2072. return -1;
  2073. }
  2074. return 0;
  2075. }
  2076. gint rename_force(const gchar *oldpath, const gchar *newpath)
  2077. {
  2078. #ifndef G_OS_UNIX
  2079. if (!is_file_entry_exist(oldpath)) {
  2080. errno = ENOENT;
  2081. return -1;
  2082. }
  2083. if (is_file_exist(newpath)) {
  2084. if (claws_unlink(newpath) < 0)
  2085. FILE_OP_ERROR(newpath, "unlink");
  2086. }
  2087. #endif
  2088. return g_rename(oldpath, newpath);
  2089. }
  2090. /*
  2091. * Append src file body to the tail of dest file.
  2092. * Now keep_backup has no effects.
  2093. */
  2094. gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
  2095. {
  2096. FILE *src_fp, *dest_fp;
  2097. gint n_read;
  2098. gchar buf[BUFSIZ];
  2099. gboolean err = FALSE;
  2100. if ((src_fp = g_fopen(src, "rb")) == NULL) {
  2101. FILE_OP_ERROR(src, "g_fopen");
  2102. return -1;
  2103. }
  2104. if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
  2105. FILE_OP_ERROR(dest, "g_fopen");
  2106. fclose(src_fp);
  2107. return -1;
  2108. }
  2109. if (change_file_mode_rw(dest_fp, dest) < 0) {
  2110. FILE_OP_ERROR(dest, "chmod");
  2111. g_warning("can't change file mode\n");
  2112. }
  2113. while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
  2114. if (n_read < sizeof(buf) && ferror(src_fp))
  2115. break;
  2116. if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
  2117. g_warning("writing to %s failed.\n", dest);
  2118. fclose(dest_fp);
  2119. fclose(src_fp);
  2120. claws_unlink(dest);
  2121. return -1;
  2122. }
  2123. }
  2124. if (ferror(src_fp)) {
  2125. FILE_OP_ERROR(src, "fread");
  2126. err = TRUE;
  2127. }
  2128. fclose(src_fp);
  2129. if (fclose(dest_fp) == EOF) {
  2130. FILE_OP_ERROR(dest, "fclose");
  2131. err = TRUE;
  2132. }
  2133. if (err) {
  2134. claws_unlink(dest);
  2135. return -1;
  2136. }
  2137. return 0;
  2138. }
  2139. gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
  2140. {
  2141. FILE *src_fp, *dest_fp;
  2142. gint n_read;
  2143. gchar buf[BUFSIZ];
  2144. gchar *dest_bak = NULL;
  2145. gboolean err = FALSE;
  2146. if ((src_fp = g_fopen(src, "rb")) == NULL) {
  2147. FILE_OP_ERROR(src, "g_fopen");
  2148. return -1;
  2149. }
  2150. if (is_file_exist(dest)) {
  2151. dest_bak = g_strconcat(dest, ".bak", NULL);
  2152. if (rename_force(dest, dest_bak) < 0) {
  2153. FILE_OP_ERROR(dest, "rename");
  2154. fclose(src_fp);
  2155. g_free(dest_bak);
  2156. return -1;
  2157. }
  2158. }
  2159. if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
  2160. FILE_OP_ERROR(dest, "g_fopen");
  2161. fclose(src_fp);
  2162. if (dest_bak) {
  2163. if (rename_force(dest_bak, dest) < 0)
  2164. FILE_OP_ERROR(dest_bak, "rename");
  2165. g_free(dest_bak);
  2166. }
  2167. return -1;
  2168. }
  2169. if (change_file_mode_rw(dest_fp, dest) < 0) {
  2170. FILE_OP_ERROR(dest, "chmod");
  2171. g_warning("can't change file mode\n");
  2172. }
  2173. while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
  2174. if (n_read < sizeof(buf) && ferror(src_fp))
  2175. break;
  2176. if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
  2177. g_warning("writing to %s failed.\n", dest);
  2178. fclose(dest_fp);
  2179. fclose(src_fp);
  2180. claws_unlink(dest);
  2181. if (dest_bak) {
  2182. if (rename_force(dest_bak, dest) < 0)
  2183. FILE_OP_ERROR(dest_bak, "rename");
  2184. g_free(dest_bak);
  2185. }
  2186. return -1;
  2187. }
  2188. }
  2189. if (ferror(src_fp)) {
  2190. FILE_OP_ERROR(src, "fread");
  2191. err = TRUE;
  2192. }
  2193. fclose(src_fp);
  2194. if (fclose(dest_fp) == EOF) {
  2195. FILE_OP_ERROR(dest, "fclose");
  2196. err = TRUE;
  2197. }
  2198. if (err) {
  2199. claws_unlink(dest);
  2200. if (dest_bak) {
  2201. if (rename_force(dest_bak, dest) < 0)
  2202. FILE_OP_ERROR(dest_bak, "rename");
  2203. g_free(dest_bak);
  2204. }
  2205. return -1;
  2206. }
  2207. if (keep_backup == FALSE && dest_bak)
  2208. claws_unlink(dest_bak);
  2209. g_free(dest_bak);
  2210. return 0;
  2211. }
  2212. gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
  2213. {
  2214. if (overwrite == FALSE && is_file_exist(dest)) {
  2215. g_warning("move_file(): file %s already exists.", dest);
  2216. return -1;
  2217. }
  2218. if (rename_force(src, dest) == 0) return 0;
  2219. if (EXDEV != errno) {
  2220. FILE_OP_ERROR(src, "rename");
  2221. return -1;
  2222. }
  2223. if (copy_file(src, dest, FALSE) < 0) return -1;
  2224. claws_unlink(src);
  2225. return 0;
  2226. }
  2227. gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
  2228. {
  2229. gint n_read;
  2230. gint bytes_left, to_read;
  2231. gchar buf[BUFSIZ];
  2232. if (fseek(fp, offset, SEEK_SET) < 0) {
  2233. perror("fseek");
  2234. return -1;
  2235. }
  2236. bytes_left = length;
  2237. to_read = MIN(bytes_left, sizeof(buf));
  2238. while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
  2239. if (n_read < to_read && ferror(fp))
  2240. break;
  2241. if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
  2242. return -1;
  2243. }
  2244. bytes_left -= n_read;
  2245. if (bytes_left == 0)
  2246. break;
  2247. to_read = MIN(bytes_left, sizeof(buf));
  2248. }
  2249. if (ferror(fp)) {
  2250. perror("fread");
  2251. return -1;
  2252. }
  2253. return 0;
  2254. }
  2255. gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
  2256. {
  2257. FILE *dest_fp;
  2258. gboolean err = FALSE;
  2259. if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
  2260. FILE_OP_ERROR(dest, "g_fopen");
  2261. return -1;
  2262. }
  2263. if (change_file_mode_rw(dest_fp, dest) < 0) {
  2264. FILE_OP_ERROR(dest, "chmod");
  2265. g_warning("can't change file mode\n");
  2266. }
  2267. if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
  2268. err = TRUE;
  2269. if (!err && fclose(dest_fp) == EOF) {
  2270. FILE_OP_ERROR(dest, "fclose");
  2271. err = TRUE;
  2272. }
  2273. if (err) {
  2274. g_warning("writing to %s failed.\n", dest);
  2275. claws_unlink(dest);
  2276. return -1;
  2277. }
  2278. return 0;
  2279. }
  2280. /* convert line endings into CRLF. If the last line doesn't end with
  2281. * linebreak, add it.
  2282. */
  2283. gchar *canonicalize_str(const gchar *str)
  2284. {
  2285. const gchar *p;
  2286. guint new_len = 0;
  2287. gchar *out, *outp;
  2288. for (p = str; *p != '\0'; ++p) {
  2289. if (*p != '\r') {
  2290. ++new_len;
  2291. if (*p == '\n')
  2292. ++new_len;
  2293. }
  2294. }
  2295. if (p == str || *(p - 1) != '\n')
  2296. new_len += 2;
  2297. out = outp = g_malloc(new_len + 1);
  2298. for (p = str; *p != '\0'; ++p) {
  2299. if (*p != '\r') {
  2300. if (*p == '\n')
  2301. *outp++ = '\r';
  2302. *outp++ = *p;
  2303. }
  2304. }
  2305. if (p == str || *(p - 1) != '\n') {
  2306. *outp++ = '\r';
  2307. *outp++ = '\n';
  2308. }
  2309. *outp = '\0';
  2310. return out;
  2311. }
  2312. gint canonicalize_file(const gchar *src, const gchar *dest)
  2313. {
  2314. FILE *src_fp, *dest_fp;
  2315. gchar buf[BUFFSIZE];
  2316. gint len;
  2317. gboolean err = FALSE;
  2318. gboolean last_linebreak = FALSE;
  2319. if (src == NULL || dest == NULL)
  2320. return -1;
  2321. if ((src_fp = g_fopen(src, "rb")) == NULL) {
  2322. FILE_OP_ERROR(src, "g_fopen");
  2323. return -1;
  2324. }
  2325. if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
  2326. FILE_OP_ERROR(dest, "g_fopen");
  2327. fclose(src_fp);
  2328. return -1;
  2329. }
  2330. if (change_file_mode_rw(dest_fp, dest) < 0) {
  2331. FILE_OP_ERROR(dest, "chmod");
  2332. g_warning("can't change file mode\n");
  2333. }
  2334. while (fgets(buf, sizeof(buf), src_fp) != NULL) {
  2335. gint r = 0;
  2336. len = strlen(buf);
  2337. if (len == 0) break;
  2338. last_linebreak = FALSE;
  2339. if (buf[len - 1] != '\n') {
  2340. last_linebreak = TRUE;
  2341. r = fputs(buf, dest_fp);
  2342. } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
  2343. r = fputs(buf, dest_fp);
  2344. } else {
  2345. if (len > 1) {
  2346. r = fwrite(buf, 1, len - 1, dest_fp);
  2347. if (r != (len -1))
  2348. r = EOF;
  2349. }
  2350. if (r != EOF)
  2351. r = fputs("\r\n", dest_fp);
  2352. }
  2353. if (r == EOF) {
  2354. g_warning("writing to %s failed.\n", dest);
  2355. fclose(dest_fp);
  2356. fclose(src_fp);
  2357. claws_unlink(dest);
  2358. return -1;
  2359. }
  2360. }
  2361. if (last_linebreak == TRUE) {
  2362. if (fputs("\r\n", dest_fp) == EOF)
  2363. err = TRUE;
  2364. }
  2365. if (ferror(src_fp)) {
  2366. FILE_OP_ERROR(src, "fgets");
  2367. err = TRUE;
  2368. }
  2369. fclose(src_fp);
  2370. if (fclose(dest_fp) == EOF) {
  2371. FILE_OP_ERROR(dest, "fclose");
  2372. err = TRUE;
  2373. }
  2374. if (err) {
  2375. claws_unlink(dest);
  2376. return -1;
  2377. }
  2378. return 0;
  2379. }
  2380. gint canonicalize_file_replace(const gchar *file)
  2381. {
  2382. gchar *tmp_file;
  2383. tmp_file = get_tmp_file();
  2384. if (canonicalize_file(file, tmp_file) < 0) {
  2385. g_free(tmp_file);
  2386. return -1;
  2387. }
  2388. if (move_file(tmp_file, file, TRUE) < 0) {
  2389. g_warning("can't replace %s .\n", file);
  2390. claws_unlink(tmp_file);
  2391. g_free(tmp_file);
  2392. return -1;
  2393. }
  2394. g_free(tmp_file);
  2395. return 0;
  2396. }
  2397. gchar *normalize_newlines(const gchar *str)
  2398. {
  2399. const gchar *p = str;
  2400. gchar *out, *outp;
  2401. out = outp = g_malloc(strlen(str) + 1);
  2402. for (p = str; *p != '\0'; ++p) {
  2403. if (*p == '\r') {
  2404. if (*(p + 1) != '\n')
  2405. *outp++ = '\n';
  2406. } else
  2407. *outp++ = *p;
  2408. }
  2409. *outp = '\0';
  2410. return out;
  2411. }
  2412. gchar *get_outgoing_rfc2822_str(FILE *fp)
  2413. {
  2414. gchar buf[BUFFSIZE];
  2415. GString *str;
  2416. gchar *ret;
  2417. str = g_string_new(NULL);
  2418. /* output header part */
  2419. while (fgets(buf, sizeof(buf), fp) != NULL) {
  2420. strretchomp(buf);
  2421. if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
  2422. gint next;
  2423. for (;;) {
  2424. next = fgetc(fp);
  2425. if (next == EOF)
  2426. break;
  2427. else if (next != ' ' && next != '\t') {
  2428. ungetc(next, fp);
  2429. break;
  2430. }
  2431. if (fgets(buf, sizeof(buf), fp) == NULL)
  2432. break;
  2433. }
  2434. } else {
  2435. g_string_append(str, buf);
  2436. g_string_append(str, "\r\n");
  2437. if (buf[0] == '\0')
  2438. break;
  2439. }
  2440. }
  2441. /* output body part */
  2442. while (fgets(buf, sizeof(buf), fp) != NULL) {
  2443. strretchomp(buf);
  2444. if (buf[0] == '.')
  2445. g_string_append_c(str, '.');
  2446. g_string_append(str, buf);
  2447. g_string_append(str, "\r\n");
  2448. }
  2449. ret = str->str;
  2450. g_string_free(str, FALSE);
  2451. return ret;
  2452. }
  2453. /*
  2454. * Create a new boundary in a way that it is very unlikely that this
  2455. * will occur in the following text. It would be easy to ensure
  2456. * uniqueness if everything is either quoted-printable or base64
  2457. * encoded (note that conversion is allowed), but because MIME bodies
  2458. * may be nested, it may happen that the same boundary has already
  2459. * been used.
  2460. *
  2461. * boundary := 0*69<bchars> bcharsnospace
  2462. * bchars := bcharsnospace / " "
  2463. * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
  2464. * "+" / "_" / "," / "-" / "." /
  2465. * "/" / ":" / "=" / "?"
  2466. *
  2467. * some special characters removed because of buggy MTAs
  2468. */
  2469. gchar *generate_mime_boundary(const gchar *prefix)
  2470. {
  2471. static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  2472. "abcdefghijklmnopqrstuvwxyz"
  2473. "1234567890+_./=";
  2474. gchar buf_uniq[24];
  2475. gint i;
  2476. for (i = 0; i < sizeof(buf_uniq) - 1; i++)
  2477. buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
  2478. buf_uniq[i] = '\0';
  2479. return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
  2480. buf_uniq);
  2481. }
  2482. gint change_file_mode_rw(FILE *fp, const gchar *file)
  2483. {
  2484. #if HAVE_FCHMOD
  2485. return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
  2486. #else
  2487. return g_chmod(file, S_IRUSR|S_IWUSR);
  2488. #endif
  2489. }
  2490. FILE *my_tmpfile(void)
  2491. {
  2492. #if HAVE_MKSTEMP || defined(G_OS_WIN32)
  2493. const gchar suffix[] = ".XXXXXX";
  2494. const gchar *tmpdir;
  2495. guint tmplen;
  2496. const gchar *progname;
  2497. guint proglen;
  2498. gchar *fname;
  2499. gint fd;
  2500. FILE *fp;
  2501. #ifndef G_OS_WIN32
  2502. gchar buf[2]="\0";
  2503. #endif
  2504. tmpdir = get_tmp_dir();
  2505. tmplen = strlen(tmpdir);
  2506. progname = g_get_prgname();
  2507. if (progname == NULL)
  2508. progname = "claws-mail";
  2509. proglen = strlen(progname);
  2510. Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
  2511. return tmpfile());
  2512. memcpy(fname, tmpdir, tmplen);
  2513. fname[tmplen] = G_DIR_SEPARATOR;
  2514. memcpy(fname + tmplen + 1, progname, proglen);
  2515. memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
  2516. fd = mkstemp(fname);
  2517. if (fd < 0)
  2518. return tmpfile();
  2519. #ifndef G_OS_WIN32
  2520. claws_unlink(fname);
  2521. /* verify that we can write in the file after unlinking */
  2522. if (write(fd, buf, 1) < 0) {
  2523. close(fd);
  2524. return tmpfile();
  2525. }
  2526. #endif
  2527. fp = fdopen(fd, "w+b");
  2528. if (!fp)
  2529. close(fd);
  2530. else {
  2531. rewind(fp);
  2532. return fp;
  2533. }
  2534. #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
  2535. return tmpfile();
  2536. }
  2537. FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
  2538. {
  2539. int fd;
  2540. #ifdef G_OS_WIN32
  2541. char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
  2542. dir, G_DIR_SEPARATOR);
  2543. fd = mkstemp_name(template, filename);
  2544. g_free(template);
  2545. #else
  2546. *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
  2547. fd = mkstemp(*filename);
  2548. #endif
  2549. return fdopen(fd, "w+");
  2550. }
  2551. FILE *str_open_as_stream(const gchar *str)
  2552. {
  2553. FILE *fp;
  2554. size_t len;
  2555. cm_return_val_if_fail(str != NULL, NULL);
  2556. fp = my_tmpfile();
  2557. if (!fp) {
  2558. FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
  2559. return NULL;
  2560. }
  2561. len = strlen(str);
  2562. if (len == 0) return fp;
  2563. if (fwrite(str, 1, len, fp) != len) {
  2564. FILE_OP_ERROR("str_open_as_stream", "fwrite");
  2565. fclose(fp);
  2566. return NULL;
  2567. }
  2568. rewind(fp);
  2569. return fp;
  2570. }
  2571. gint str_write_to_file(const gchar *str, const gchar *file)
  2572. {
  2573. FILE *fp;
  2574. size_t len;
  2575. cm_return_val_if_fail(str != NULL, -1);
  2576. cm_return_val_if_fail(file != NULL, -1);
  2577. if ((fp = g_fopen(file, "wb")) == NULL) {
  2578. FILE_OP_ERROR(file, "g_fopen");
  2579. return -1;
  2580. }
  2581. len = strlen(str);
  2582. if (len == 0) {
  2583. fclose(fp);
  2584. return 0;
  2585. }
  2586. if (fwrite(str, 1, len, fp) != len) {
  2587. FILE_OP_ERROR(file, "fwrite");
  2588. fclose(fp);
  2589. claws_unlink(file);
  2590. return -1;
  2591. }
  2592. if (fclose(fp) == EOF) {
  2593. FILE_OP_ERROR(file, "fclose");
  2594. claws_unlink(file);
  2595. return -1;
  2596. }
  2597. return 0;
  2598. }
  2599. static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
  2600. {
  2601. GByteArray *array;
  2602. guchar buf[BUFSIZ];
  2603. gint n_read;
  2604. gchar *str;
  2605. cm_return_val_if_fail(fp != NULL, NULL);
  2606. array = g_byte_array_new();
  2607. while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
  2608. if (n_read < sizeof(buf) && ferror(fp))
  2609. break;
  2610. g_byte_array_append(array, buf, n_read);
  2611. }
  2612. if (ferror(fp)) {
  2613. FILE_OP_ERROR("file stream", "fread");
  2614. g_byte_array_free(array, TRUE);
  2615. return NULL;
  2616. }
  2617. buf[0] = '\0';
  2618. g_byte_array_append(array, buf, 1);
  2619. str = (gchar *)array->data;
  2620. g_byte_array_free(array, FALSE);
  2621. if (recode && !g_utf8_validate(str, -1, NULL)) {
  2622. const gchar *src_codeset, *dest_codeset;
  2623. gchar *tmp = NULL;
  2624. src_codeset = conv_get_locale_charset_str();
  2625. dest_codeset = CS_UTF_8;
  2626. tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
  2627. g_free(str);
  2628. str = tmp;
  2629. }
  2630. return str;
  2631. }
  2632. static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
  2633. {
  2634. FILE *fp;
  2635. gchar *str;
  2636. struct stat s;
  2637. #ifndef G_OS_WIN32
  2638. gint fd, err;
  2639. struct timeval timeout = {1, 0};
  2640. fd_set fds;
  2641. int fflags = 0;
  2642. #endif
  2643. cm_return_val_if_fail(file != NULL, NULL);
  2644. if (g_stat(file, &s) != 0) {
  2645. FILE_OP_ERROR(file, "stat");
  2646. return NULL;
  2647. }
  2648. if (S_ISDIR(s.st_mode)) {
  2649. g_warning("%s: is a directory\n", file);
  2650. return NULL;
  2651. }
  2652. #ifdef G_OS_WIN32
  2653. fp = g_fopen (file, "rb");
  2654. if (fp == NULL) {
  2655. FILE_OP_ERROR(file, "open");
  2656. return NULL;
  2657. }
  2658. #else
  2659. /* test whether the file is readable without blocking */
  2660. fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
  2661. if (fd == -1) {
  2662. FILE_OP_ERROR(file, "open");
  2663. return NULL;
  2664. }
  2665. FD_ZERO(&fds);
  2666. FD_SET(fd, &fds);
  2667. /* allow for one second */
  2668. err = select(fd+1, &fds, NULL, NULL, &timeout);
  2669. if (err <= 0 || !FD_ISSET(fd, &fds)) {
  2670. if (err < 0) {
  2671. FILE_OP_ERROR(file, "select");
  2672. } else {
  2673. g_warning("%s: doesn't seem readable\n", file);
  2674. }
  2675. close(fd);
  2676. return NULL;
  2677. }
  2678. /* Now clear O_NONBLOCK */
  2679. if ((fflags = fcntl(fd, F_GETFL)) < 0) {
  2680. FILE_OP_ERROR(file, "fcntl (F_GETFL)");
  2681. close(fd);
  2682. return NULL;
  2683. }
  2684. if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
  2685. FILE_OP_ERROR(file, "fcntl (F_SETFL)");
  2686. close(fd);
  2687. return NULL;
  2688. }
  2689. /* get the FILE pointer */
  2690. fp = fdopen(fd, "rb");
  2691. if (fp == NULL) {
  2692. FILE_OP_ERROR(file, "fdopen");
  2693. close(fd); /* if fp isn't NULL, we'll use fclose instead! */
  2694. return NULL;
  2695. }
  2696. #endif
  2697. str = file_read_stream_to_str_full(fp, recode);
  2698. fclose(fp);
  2699. return str;
  2700. }
  2701. gchar *file_read_to_str(const gchar *file)
  2702. {
  2703. return file_read_to_str_full(file, TRUE);
  2704. }
  2705. gchar *file_read_stream_to_str(FILE *fp)
  2706. {
  2707. return file_read_stream_to_str_full(fp, TRUE);
  2708. }
  2709. gchar *file_read_to_str_no_recode(const gchar *file)
  2710. {
  2711. return file_read_to_str_full(file, FALSE);
  2712. }
  2713. gchar *file_read_stream_to_str_no_recode(FILE *fp)
  2714. {
  2715. return file_read_stream_to_str_full(fp, FALSE);
  2716. }
  2717. char *fgets_crlf(char *buf, int size, FILE *stream)
  2718. {
  2719. gboolean is_cr = FALSE;
  2720. gboolean last_was_cr = FALSE;
  2721. int c = 0;
  2722. char *cs;
  2723. cs = buf;
  2724. while (--size > 0 && (c = getc(stream)) != EOF)
  2725. {
  2726. *cs++ = c;
  2727. is_cr = (c == '\r');
  2728. if (c == '\n') {
  2729. break;
  2730. }
  2731. if (last_was_cr) {
  2732. *(--cs) = '\n';
  2733. cs++;
  2734. ungetc(c, stream);
  2735. break;
  2736. }
  2737. last_was_cr = is_cr;
  2738. }
  2739. if (c == EOF && cs == buf)
  2740. return NULL;
  2741. *cs = '\0';
  2742. return buf;
  2743. }
  2744. static gint execute_async(gchar *const argv[])
  2745. {
  2746. cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
  2747. if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
  2748. NULL, NULL, NULL, FALSE) == FALSE) {
  2749. g_warning("Couldn't execute command: %s\n", argv[0]);
  2750. return -1;
  2751. }
  2752. return 0;
  2753. }
  2754. static gint execute_sync(gchar *const argv[])
  2755. {
  2756. gint status;
  2757. cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
  2758. #ifdef G_OS_UNIX
  2759. if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
  2760. NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
  2761. g_warning("Couldn't execute command: %s\n", argv[0]);
  2762. return -1;
  2763. }
  2764. if (WIFEXITED(status))
  2765. return WEXITSTATUS(status);
  2766. else
  2767. return -1;
  2768. #else
  2769. if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH|
  2770. G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
  2771. NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
  2772. g_warning("Couldn't execute command: %s\n", argv[0]);
  2773. return -1;
  2774. }
  2775. return status;
  2776. #endif
  2777. }
  2778. gint execute_command_line(const gchar *cmdline, gboolean async)
  2779. {
  2780. gchar **argv;
  2781. gint ret;
  2782. debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
  2783. argv = strsplit_with_quote(cmdline, " ", 0);
  2784. if (async)
  2785. ret = execute_async(argv);
  2786. else
  2787. ret = execute_sync(argv);
  2788. g_strfreev(argv);
  2789. return ret;
  2790. }
  2791. gchar *get_command_output(const gchar *cmdline)
  2792. {
  2793. gchar *child_stdout;
  2794. gint status;
  2795. cm_return_val_if_fail(cmdline != NULL, NULL);
  2796. debug_print("get_command_output(): executing: %s\n", cmdline);
  2797. if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
  2798. NULL) == FALSE) {
  2799. g_warning("Couldn't execute command: %s\n", cmdline);
  2800. return NULL;
  2801. }
  2802. return child_stdout;
  2803. }
  2804. #ifndef MAEMO
  2805. static gint is_unchanged_uri_char(char c)
  2806. {
  2807. switch (c) {
  2808. case '(':
  2809. case ')':
  2810. return 0;
  2811. default:
  2812. return 1;
  2813. }
  2814. }
  2815. static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
  2816. {
  2817. int i;
  2818. int k;
  2819. k = 0;
  2820. for(i = 0; i < strlen(uri) ; i++) {
  2821. if (is_unchanged_uri_char(uri[i])) {
  2822. if (k + 2 >= bufsize)
  2823. break;
  2824. encoded_uri[k++] = uri[i];
  2825. }
  2826. else {
  2827. char * hexa = "0123456789ABCDEF";
  2828. if (k + 4 >= bufsize)
  2829. break;
  2830. encoded_uri[k++] = '%';
  2831. encoded_uri[k++] = hexa[uri[i] / 16];
  2832. encoded_uri[k++] = hexa[uri[i] % 16];
  2833. }
  2834. }
  2835. encoded_uri[k] = 0;
  2836. }
  2837. #endif
  2838. gint open_uri(const gchar *uri, const gchar *cmdline)
  2839. {
  2840. #ifndef MAEMO
  2841. #ifndef G_OS_WIN32
  2842. gchar buf[BUFFSIZE];
  2843. gchar *p;
  2844. gchar encoded_uri[BUFFSIZE];
  2845. cm_return_val_if_fail(uri != NULL, -1);
  2846. /* an option to choose whether to use encode_uri or not ? */
  2847. encode_uri(encoded_uri, BUFFSIZE, uri);
  2848. if (cmdline &&
  2849. (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
  2850. !strchr(p + 2, '%'))
  2851. g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
  2852. else {
  2853. if (cmdline)
  2854. g_warning("Open URI command-line is invalid "
  2855. "(there must be only one '%%s'): %s",
  2856. cmdline);
  2857. g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
  2858. }
  2859. execute_command_line(buf, TRUE);
  2860. #else
  2861. ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
  2862. #endif
  2863. #else
  2864. extern osso_context_t *get_osso_context(void);
  2865. osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
  2866. OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
  2867. DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
  2868. #endif
  2869. return 0;
  2870. }
  2871. gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
  2872. {
  2873. gchar buf[BUFFSIZE];
  2874. gchar *p;
  2875. cm_return_val_if_fail(filepath != NULL, -1);
  2876. if (cmdline &&
  2877. (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
  2878. !strchr(p + 2, '%'))
  2879. g_snprintf(buf, sizeof(buf), cmdline, filepath);
  2880. else {
  2881. if (cmdline)
  2882. g_warning("Open Text Editor command-line is invalid "
  2883. "(there must be only one '%%s'): %s",
  2884. cmdline);
  2885. g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
  2886. }
  2887. execute_command_line(buf, TRUE);
  2888. return 0;
  2889. }
  2890. time_t remote_tzoffset_sec(const gchar *zone)
  2891. {
  2892. static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
  2893. gchar zone3[4];
  2894. gchar *p;
  2895. gchar c;
  2896. gint iustz;
  2897. gint offset;
  2898. time_t remoteoffset;
  2899. strncpy(zone3, zone, 3);
  2900. zone3[3] = '\0';
  2901. remoteoffset = 0;
  2902. if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
  2903. (c == '+' || c == '-')) {
  2904. remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
  2905. if (c == '-')
  2906. remoteoffset = -remoteoffset;
  2907. } else if (!strncmp(zone, "UT" , 2) ||
  2908. !strncmp(zone, "GMT", 2)) {
  2909. remoteoffset = 0;
  2910. } else if (strlen(zone3) == 3) {
  2911. for (p = ustzstr; *p != '\0'; p += 3) {
  2912. if (!g_ascii_strncasecmp(p, zone3, 3)) {
  2913. iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
  2914. remoteoffset = iustz * 3600;
  2915. break;
  2916. }
  2917. }
  2918. if (*p == '\0')
  2919. return -1;
  2920. } else if (strlen(zone3) == 1) {
  2921. switch (zone[0]) {
  2922. case 'Z': remoteoffset = 0; break;
  2923. case 'A': remoteoffset = -1; break;
  2924. case 'B': remoteoffset = -2; break;
  2925. case 'C': remoteoffset = -3; break;
  2926. case 'D': remoteoffset = -4; break;
  2927. case 'E': remoteoffset = -5; break;
  2928. case 'F': remoteoffset = -6; break;
  2929. case 'G': remoteoffset = -7; break;
  2930. case 'H': remoteoffset = -8; break;
  2931. case 'I': remoteoffset = -9; break;
  2932. case 'K': remoteoffset = -10; break; /* J is not used */
  2933. case 'L': remoteoffset = -11; break;
  2934. case 'M': remoteoffset = -12; break;
  2935. case 'N': remoteoffset = 1; break;
  2936. case 'O': remoteoffset = 2; break;
  2937. case 'P': remoteoffset = 3; break;
  2938. case 'Q': remoteoffset = 4; break;
  2939. case 'R': remoteoffset = 5; break;
  2940. case 'S': remoteoffset = 6; break;
  2941. case 'T': remoteoffset = 7; break;
  2942. case 'U': remoteoffset = 8; break;
  2943. case 'V': remoteoffset = 9; break;
  2944. case 'W': remoteoffset = 10; break;
  2945. case 'X': remoteoffset = 11; break;
  2946. case 'Y': remoteoffset = 12; break;
  2947. default: remoteoffset = 0; break;
  2948. }
  2949. remoteoffset = remoteoffset * 3600;
  2950. } else
  2951. return -1;
  2952. return remoteoffset;
  2953. }
  2954. time_t tzoffset_sec(time_t *now)
  2955. {
  2956. struct tm gmt, *lt;
  2957. gint off;
  2958. struct tm buf1, buf2;
  2959. #ifdef G_OS_WIN32
  2960. if (now && *now < 0)
  2961. return 0;
  2962. #endif
  2963. gmt = *gmtime_r(now, &buf1);
  2964. lt = localtime_r(now, &buf2);
  2965. off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
  2966. if (lt->tm_year < gmt.tm_year)
  2967. off -= 24 * 60;
  2968. else if (lt->tm_year > gmt.tm_year)
  2969. off += 24 * 60;
  2970. else if (lt->tm_yday < gmt.tm_yday)
  2971. off -= 24 * 60;
  2972. else if (lt->tm_yday > gmt.tm_yday)
  2973. off += 24 * 60;
  2974. if (off >= 24 * 60) /* should be impossible */
  2975. off = 23 * 60 + 59; /* if not, insert silly value */
  2976. if (off <= -24 * 60)
  2977. off = -(23 * 60 + 59);
  2978. return off * 60;
  2979. }
  2980. /* calculate timezone offset */
  2981. gchar *tzoffset(time_t *now)
  2982. {
  2983. static gchar offset_string[6];
  2984. struct tm gmt, *lt;
  2985. gint off;
  2986. gchar sign = '+';
  2987. struct tm buf1, buf2;
  2988. #ifdef G_OS_WIN32
  2989. if (now && *now < 0)
  2990. return 0;
  2991. #endif
  2992. gmt = *gmtime_r(now, &buf1);
  2993. lt = localtime_r(now, &buf2);
  2994. off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
  2995. if (lt->tm_year < gmt.tm_year)
  2996. off -= 24 * 60;
  2997. else if (lt->tm_year > gmt.tm_year)
  2998. off += 24 * 60;
  2999. else if (lt->tm_yday < gmt.tm_yday)
  3000. off -= 24 * 60;
  3001. else if (lt->tm_yday > gmt.tm_yday)
  3002. off += 24 * 60;
  3003. if (off < 0) {
  3004. sign = '-';
  3005. off = -off;
  3006. }
  3007. if (off >= 24 * 60) /* should be impossible */
  3008. off = 23 * 60 + 59; /* if not, insert silly value */
  3009. sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
  3010. return offset_string;
  3011. }
  3012. void get_rfc822_date(gchar *buf, gint len)
  3013. {
  3014. struct tm *lt;
  3015. time_t t;
  3016. gchar day[4], mon[4];
  3017. gint dd, hh, mm, ss, yyyy;
  3018. struct tm buf1;
  3019. gchar buf2[BUFFSIZE];
  3020. t = time(NULL);
  3021. lt = localtime_r(&t, &buf1);
  3022. sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
  3023. day, mon, &dd, &hh, &mm, &ss, &yyyy);
  3024. g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
  3025. day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
  3026. }
  3027. void debug_set_mode(gboolean mode)
  3028. {
  3029. debug_mode = mode;
  3030. }
  3031. gboolean debug_get_mode(void)
  3032. {
  3033. return debug_mode;
  3034. }
  3035. void debug_print_real(const gchar *format, ...)
  3036. {
  3037. va_list args;
  3038. gchar buf[BUFFSIZE];
  3039. if (!debug_mode) return;
  3040. va_start(args, format);
  3041. g_vsnprintf(buf, sizeof(buf), format, args);
  3042. va_end(args);
  3043. g_print("%s", buf);
  3044. }
  3045. const char * debug_srcname(const char *file)
  3046. {
  3047. const char *s = strrchr (file, '/');
  3048. return s? s+1:file;
  3049. }
  3050. void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
  3051. {
  3052. if (subject == NULL)
  3053. subject = "";
  3054. else
  3055. subject += subject_get_prefix_length(subject);
  3056. return g_hash_table_lookup(subject_table, subject);
  3057. }
  3058. void subject_table_insert(GHashTable *subject_table, gchar * subject,
  3059. void * data)
  3060. {
  3061. if (subject == NULL || *subject == 0)
  3062. return;
  3063. subject += subject_get_prefix_length(subject);
  3064. g_hash_table_insert(subject_table, subject, data);
  3065. }
  3066. void subject_table_remove(GHashTable *subject_table, gchar * subject)
  3067. {
  3068. if (subject == NULL)
  3069. return;
  3070. subject += subject_get_prefix_length(subject);
  3071. g_hash_table_remove(subject_table, subject);
  3072. }
  3073. #ifndef G_OS_WIN32
  3074. static regex_t u_regex;
  3075. static gboolean u_init_;
  3076. #endif
  3077. void utils_free_regex(void)
  3078. {
  3079. #ifndef G_OS_WIN32
  3080. if (u_init_) {
  3081. regfree(&u_regex);
  3082. u_init_ = FALSE;
  3083. }
  3084. #endif
  3085. }
  3086. /*!
  3087. *\brief Check if a string is prefixed with known (combinations)
  3088. * of prefixes. The function assumes that each prefix
  3089. * is terminated by zero or exactly _one_ space.
  3090. *
  3091. *\param str String to check for a prefixes
  3092. *
  3093. *\return int Number of chars in the prefix that should be skipped
  3094. * for a "clean" subject line. If no prefix was found, 0
  3095. * is returned.
  3096. */
  3097. int subject_get_prefix_length(const gchar *subject)
  3098. {
  3099. #ifndef G_OS_WIN32
  3100. /*!< Array with allowable reply prefixes regexps. */
  3101. static const gchar * const prefixes[] = {
  3102. "Re\\:", /* "Re:" */
  3103. "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
  3104. "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
  3105. "Aw\\:", /* "Aw:" (German) */
  3106. "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
  3107. "Res\\:", /* "Res:" (Spanish/Brazilian Outlook) */
  3108. "Fw\\:", /* "Fw:" Forward */
  3109. "Fwd\\:", /* "Fwd:" Forward */
  3110. "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
  3111. "Odp\\:", /* "Odp:" Re (Polish Outlook) */
  3112. "Rif\\:", /* "Rif:" (Italian Outlook) */
  3113. "Sv\\:", /* "Sv" (Norwegian) */
  3114. "Vs\\:", /* "Vs" (Norwegian) */
  3115. "Ad\\:", /* "Ad" (Norwegian) */
  3116. "\347\255\224\345\244\215\\:", /* "Re" (Chinese, UTF-8) */
  3117. "R\303\251f\\. \\:", /* "R?f. :" (French Lotus Notes) */
  3118. "Re \\:", /* "Re :" (French Yahoo Mail) */
  3119. /* add more */
  3120. };
  3121. const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
  3122. int n;
  3123. regmatch_t pos;
  3124. if (!subject) return 0;
  3125. if (!*subject) return 0;
  3126. if (!u_init_) {
  3127. GString *s = g_string_new("");
  3128. for (n = 0; n < PREFIXES; n++)
  3129. /* Terminate each prefix regexpression by a
  3130. * "\ ?" (zero or ONE space), and OR them */
  3131. g_string_append_printf(s, "(%s\\ ?)%s",
  3132. prefixes[n],
  3133. n < PREFIXES - 1 ?
  3134. "|" : "");
  3135. g_string_prepend(s, "(");
  3136. g_string_append(s, ")+"); /* match at least once */
  3137. g_string_prepend(s, "^\\ *"); /* from beginning of line */
  3138. /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
  3139. * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
  3140. if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
  3141. debug_print("Error compiling regexp %s\n", s->str);
  3142. g_string_free(s, TRUE);
  3143. return 0;
  3144. } else {
  3145. u_init_ = TRUE;
  3146. g_string_free(s, TRUE);
  3147. }
  3148. }
  3149. if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
  3150. return pos.rm_eo;
  3151. else
  3152. return 0;
  3153. #else
  3154. /*!< Array with allowable reply prefixes regexps. */
  3155. static const gchar * const prefixes[] = {
  3156. "re:", /* "Re:" */
  3157. "antw:", /* "Antw:" (Dutch / German Outlook) */
  3158. "aw:", /* "Aw:" (German) */
  3159. "antwort:", /* "Antwort:" (German Lotus Notes) */
  3160. "res:", /* "Res:" (Spanish/Brazilian Outlook) */
  3161. "fw:", /* "Fw:" Forward */
  3162. "fwd:", /* "Fwd:" Forward */
  3163. "enc:", /* "Enc:" Forward (Brazilian Outlook) */
  3164. "odp:", /* "Odp:" Re (Polish Outlook) */
  3165. "rif:", /* "Rif:" (Italian Outlook) */
  3166. "sv:", /* "Sv" (Norwegian) */
  3167. "vs:", /* "Vs" (Norwegian) */
  3168. "ad:", /* "Ad" (Norwegian) */
  3169. "R\303\251f. :", /* "R?f. :" (French Lotus Notes) */
  3170. "Re :", /* "Re :" (French Yahoo Mail) */
  3171. /* add more */
  3172. };
  3173. const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
  3174. int n;
  3175. if (!subject) return 0;
  3176. if (!*subject) return 0;
  3177. for (n = 0; n < PREFIXES; n++) {
  3178. int len = strlen(prefixes[n]);
  3179. if (!strncasecmp(subject, prefixes[n], len)) {
  3180. if (subject[len] == ' ')
  3181. return len+1;
  3182. else
  3183. return len;
  3184. }
  3185. }
  3186. return 0;
  3187. #endif
  3188. }
  3189. static guint g_stricase_hash(gconstpointer gptr)
  3190. {
  3191. guint hash_result = 0;
  3192. const char *str;
  3193. for (str = gptr; str && *str; str++) {
  3194. hash_result += toupper(*str);
  3195. }
  3196. return hash_result;
  3197. }
  3198. static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
  3199. {
  3200. const char *str1 = gptr1;
  3201. const char *str2 = gptr2;
  3202. return !strcasecmp(str1, str2);
  3203. }
  3204. gint g_int_compare(gconstpointer a, gconstpointer b)
  3205. {
  3206. return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
  3207. }
  3208. gchar *generate_msgid(gchar *buf, gint len, gchar *user_addr)
  3209. {
  3210. struct tm *lt;
  3211. time_t t;
  3212. gchar *addr;
  3213. struct tm buft;
  3214. t = time(NULL);
  3215. lt = localtime_r(&t, &buft);
  3216. if (user_addr != NULL)
  3217. addr = g_strdup_printf(".%s", user_addr);
  3218. else if (strlen(buf) != 0)
  3219. addr = g_strdup_printf("@%s", buf);
  3220. else
  3221. addr = g_strdup_printf("@%s", get_domain_name());
  3222. /* Replace all @ but the last one in addr, with underscores.
  3223. * RFC 2822 States that msg-id syntax only allows one @.
  3224. */
  3225. while (strchr(addr, '@') != NULL && strchr(addr, '@') != strrchr(addr, '@'))
  3226. *(strchr(addr, '@')) = '_';
  3227. g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
  3228. lt->tm_year + 1900, lt->tm_mon + 1,
  3229. lt->tm_mday, lt->tm_hour,
  3230. lt->tm_min, lt->tm_sec,
  3231. (guint) rand(), addr);
  3232. g_free(addr);
  3233. return buf;
  3234. }
  3235. /*
  3236. quote_cmd_argument()
  3237. return a quoted string safely usable in argument of a command.
  3238. code is extracted and adapted from etPan! project -- DINH V. Ho?.
  3239. */
  3240. gint quote_cmd_argument(gchar * result, guint size,
  3241. const gchar * path)
  3242. {
  3243. const gchar * p;
  3244. gchar * result_p;
  3245. guint remaining;
  3246. result_p = result;
  3247. remaining = size;
  3248. for(p = path ; * p != '\0' ; p ++) {
  3249. if (isalnum((guchar)*p) || (* p == '/')) {
  3250. if (remaining > 0) {
  3251. * result_p = * p;
  3252. result_p ++;
  3253. remaining --;
  3254. }
  3255. else {
  3256. result[size - 1] = '\0';
  3257. return -1;
  3258. }
  3259. }
  3260. else {
  3261. if (remaining >= 2) {
  3262. * result_p = '\\';
  3263. result_p ++;
  3264. * result_p = * p;
  3265. result_p ++;
  3266. remaining -= 2;
  3267. }
  3268. else {
  3269. result[size - 1] = '\0';
  3270. return -1;
  3271. }
  3272. }
  3273. }
  3274. if (remaining > 0) {
  3275. * result_p = '\0';
  3276. }
  3277. else {
  3278. result[size - 1] = '\0';
  3279. return -1;
  3280. }
  3281. return 0;
  3282. }
  3283. typedef struct
  3284. {
  3285. GNode *parent;
  3286. GNodeMapFunc func;
  3287. gpointer data;
  3288. } GNodeMapData;
  3289. static void g_node_map_recursive(GNode *node, gpointer data)
  3290. {
  3291. GNodeMapData *mapdata = (GNodeMapData *) data;
  3292. GNode *newnode;
  3293. GNodeMapData newmapdata;
  3294. gpointer newdata;
  3295. newdata = mapdata->func(node->data, mapdata->data);
  3296. if (newdata != NULL) {
  3297. newnode = g_node_new(newdata);
  3298. g_node_append(mapdata->parent, newnode);
  3299. newmapdata.parent = newnode;
  3300. newmapdata.func = mapdata->func;
  3301. newmapdata.data = mapdata->data;
  3302. g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
  3303. }
  3304. }
  3305. GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
  3306. {
  3307. GNode *root;
  3308. GNodeMapData mapdata;
  3309. cm_return_val_if_fail(node != NULL, NULL);
  3310. cm_return_val_if_fail(func != NULL, NULL);
  3311. root = g_node_new(func(node->data, data));
  3312. mapdata.parent = root;
  3313. mapdata.func = func;
  3314. mapdata.data = data;
  3315. g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
  3316. return root;
  3317. }
  3318. #define HEX_TO_INT(val, hex) \
  3319. { \
  3320. gchar c = hex; \
  3321. \
  3322. if ('0' <= c && c <= '9') { \
  3323. val = c - '0'; \
  3324. } else if ('a' <= c && c <= 'f') { \
  3325. val = c - 'a' + 10; \
  3326. } else if ('A' <= c && c <= 'F') { \
  3327. val = c - 'A' + 10; \
  3328. } else { \
  3329. val = -1; \
  3330. } \
  3331. }
  3332. gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
  3333. {
  3334. gint hi, lo;
  3335. HEX_TO_INT(hi, c1);
  3336. HEX_TO_INT(lo, c2);
  3337. if (hi == -1 || lo == -1)
  3338. return FALSE;
  3339. *out = (hi << 4) + lo;
  3340. return TRUE;
  3341. }
  3342. #define INT_TO_HEX(hex, val) \
  3343. { \
  3344. if ((val) < 10) \
  3345. hex = '0' + (val); \
  3346. else \
  3347. hex = 'A' + (val) - 10; \
  3348. }
  3349. void get_hex_str(gchar *out, guchar ch)
  3350. {
  3351. gchar hex;
  3352. INT_TO_HEX(hex, ch >> 4);
  3353. *out++ = hex;
  3354. INT_TO_HEX(hex, ch & 0x0f);
  3355. *out++ = hex;
  3356. }
  3357. #undef REF_DEBUG
  3358. #ifndef REF_DEBUG
  3359. #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
  3360. #else
  3361. #define G_PRINT_REF g_print
  3362. #endif
  3363. /*!
  3364. *\brief Register ref counted pointer. It is based on GBoxed, so should
  3365. * work with anything that uses the GType system. The semantics
  3366. * are similar to a C++ auto pointer, with the exception that
  3367. * C doesn't have automatic closure (calling destructors) when
  3368. * exiting a block scope.
  3369. * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
  3370. * function directly.
  3371. *
  3372. *\return GType A GType type.
  3373. */
  3374. GType g_auto_pointer_register(void)
  3375. {
  3376. static GType auto_pointer_type;
  3377. if (!auto_pointer_type)
  3378. auto_pointer_type =
  3379. g_boxed_type_register_static
  3380. ("G_TYPE_AUTO_POINTER",
  3381. (GBoxedCopyFunc) g_auto_pointer_copy,
  3382. (GBoxedFreeFunc) g_auto_pointer_free);
  3383. return auto_pointer_type;
  3384. }
  3385. /*!
  3386. *\brief Structure with g_new() allocated pointer guarded by the
  3387. * auto pointer
  3388. */
  3389. typedef struct AutoPointerRef {
  3390. void (*free) (gpointer);
  3391. gpointer pointer;
  3392. glong cnt;
  3393. } AutoPointerRef;
  3394. /*!
  3395. *\brief The auto pointer opaque structure that references the
  3396. * pointer guard block.
  3397. */
  3398. typedef struct AutoPointer {
  3399. AutoPointerRef *ref;
  3400. gpointer ptr; /*!< access to protected pointer */
  3401. } AutoPointer;
  3402. /*!
  3403. *\brief Creates an auto pointer for a g_new()ed pointer. Example:
  3404. *
  3405. *\code
  3406. *
  3407. * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
  3408. * ... when assigning, copying and freeing storage elements
  3409. *
  3410. * gtk_list_store_new(N_S_COLUMNS,
  3411. * G_TYPE_AUTO_POINTER,
  3412. * -1);
  3413. *
  3414. *
  3415. * Template *precious_data = g_new0(Template, 1);
  3416. * g_pointer protect = g_auto_pointer_new(precious_data);
  3417. *
  3418. * gtk_list_store_set(container, &iter,
  3419. * S_DATA, protect,
  3420. * -1);
  3421. *
  3422. * ... the gtk_list_store has copied the pointer and
  3423. * ... incremented its reference count, we should free
  3424. * ... the auto pointer (in C++ a destructor would do
  3425. * ... this for us when leaving block scope)
  3426. *
  3427. * g_auto_pointer_free(protect);
  3428. *
  3429. * ... gtk_list_store_set() now manages the data. When
  3430. * ... *explicitly* requesting a pointer from the list
  3431. * ... store, don't forget you get a copy that should be
  3432. * ... freed with g_auto_pointer_free() eventually.
  3433. *
  3434. *\endcode
  3435. *
  3436. *\param pointer Pointer to be guarded.
  3437. *
  3438. *\return GAuto * Pointer that should be used in containers with
  3439. * GType support.
  3440. */
  3441. GAuto *g_auto_pointer_new(gpointer p)
  3442. {
  3443. AutoPointerRef *ref;
  3444. AutoPointer *ptr;
  3445. if (p == NULL)
  3446. return NULL;
  3447. ref = g_new0(AutoPointerRef, 1);
  3448. ptr = g_new0(AutoPointer, 1);
  3449. ref->pointer = p;
  3450. ref->free = g_free;
  3451. ref->cnt = 1;
  3452. ptr->ref = ref;
  3453. ptr->ptr = p;
  3454. #ifdef REF_DEBUG
  3455. G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
  3456. #endif
  3457. return ptr;
  3458. }
  3459. /*!
  3460. *\brief Allocate an autopointer using the passed \a free function to
  3461. * free the guarded pointer
  3462. */
  3463. GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
  3464. {
  3465. AutoPointer *aptr;
  3466. if (p == NULL)
  3467. return NULL;
  3468. aptr = g_auto_pointer_new(p);
  3469. aptr->ref->free = free_;
  3470. return aptr;
  3471. }
  3472. gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
  3473. {
  3474. if (auto_ptr == NULL)
  3475. return NULL;
  3476. return ((AutoPointer *) auto_ptr)->ptr;
  3477. }
  3478. /*!
  3479. *\brief Copies an auto pointer by. It's mostly not necessary
  3480. * to call this function directly, unless you copy/assign
  3481. * the guarded pointer.
  3482. *
  3483. *\param auto_ptr Auto pointer returned by previous call to
  3484. * g_auto_pointer_new_XXX()
  3485. *
  3486. *\return gpointer An auto pointer
  3487. */
  3488. GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
  3489. {
  3490. AutoPointer *ptr;
  3491. AutoPointerRef *ref;
  3492. AutoPointer *newp;
  3493. if (auto_ptr == NULL)
  3494. return NULL;
  3495. ptr = auto_ptr;
  3496. ref = ptr->ref;
  3497. newp = g_new0(AutoPointer, 1);
  3498. newp->ref = ref;
  3499. newp->ptr = ref->pointer;
  3500. ++(ref->cnt);
  3501. #ifdef REF_DEBUG
  3502. G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
  3503. #endif
  3504. return newp;
  3505. }
  3506. /*!
  3507. *\brief Free an auto pointer
  3508. */
  3509. void g_auto_pointer_free(GAuto *auto_ptr)
  3510. {
  3511. AutoPointer *ptr;
  3512. AutoPointerRef *ref;
  3513. if (auto_ptr == NULL)
  3514. return;
  3515. ptr = auto_ptr;
  3516. ref = ptr->ref;
  3517. if (--(ref->cnt) == 0) {
  3518. #ifdef REF_DEBUG
  3519. G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
  3520. #endif
  3521. ref->free(ref->pointer);
  3522. g_free(ref);
  3523. }
  3524. #ifdef REF_DEBUG
  3525. else
  3526. G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
  3527. #endif
  3528. g_free(ptr);
  3529. }
  3530. void replace_returns(gchar *str)
  3531. {
  3532. if (!str)
  3533. return;
  3534. while (strstr(str, "\n")) {
  3535. *strstr(str, "\n") = ' ';
  3536. }
  3537. while (strstr(str, "\r")) {
  3538. *strstr(str, "\r") = ' ';
  3539. }
  3540. }
  3541. /* get_uri_part() - retrieves a URI starting from scanpos.
  3542. Returns TRUE if succesful */
  3543. gboolean get_uri_part(const gchar *start, const gchar *scanpos,
  3544. const gchar **bp, const gchar **ep, gboolean hdr)
  3545. {
  3546. const gchar *ep_;
  3547. gint parenthese_cnt = 0;
  3548. cm_return_val_if_fail(start != NULL, FALSE);
  3549. cm_return_val_if_fail(scanpos != NULL, FALSE);
  3550. cm_return_val_if_fail(bp != NULL, FALSE);
  3551. cm_return_val_if_fail(ep != NULL, FALSE);
  3552. *bp = scanpos;
  3553. /* find end point of URI */
  3554. for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
  3555. if (!g_ascii_isgraph(*(const guchar *)ep_) ||
  3556. !IS_ASCII(*(const guchar *)ep_) ||
  3557. strchr("[]{}<>\"", *ep_)) {
  3558. break;
  3559. } else if (strchr("(", *ep_)) {
  3560. parenthese_cnt++;
  3561. } else if (strchr(")", *ep_)) {
  3562. if (parenthese_cnt > 0)
  3563. parenthese_cnt--;
  3564. else
  3565. break;
  3566. }
  3567. }
  3568. /* no punctuation at end of string */
  3569. /* FIXME: this stripping of trailing punctuations may bite with other URIs.
  3570. * should pass some URI type to this function and decide on that whether
  3571. * to perform punctuation stripping */
  3572. #define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_)", ch))
  3573. for (; ep_ - 1 > scanpos + 1 &&
  3574. IS_REAL_PUNCT(*(ep_ - 1));
  3575. ep_--)
  3576. ;
  3577. #undef IS_REAL_PUNCT
  3578. *ep = ep_;
  3579. return TRUE;
  3580. }
  3581. gchar *make_uri_string(const gchar *bp, const gchar *ep)
  3582. {
  3583. while (bp && *bp && g_ascii_isspace(*bp))
  3584. bp++;
  3585. return g_strndup(bp, ep - bp);
  3586. }
  3587. /* valid mail address characters */
  3588. #define IS_RFC822_CHAR(ch) \
  3589. (IS_ASCII(ch) && \
  3590. (ch) > 32 && \
  3591. (ch) != 127 && \
  3592. !g_ascii_isspace(ch) && \
  3593. !strchr("(),;<>\"", (ch)))
  3594. /* alphabet and number within 7bit ASCII */
  3595. #define IS_ASCII_ALNUM(ch) (IS_ASCII(ch) && g_ascii_isalnum(ch))
  3596. #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
  3597. static GHashTable *create_domain_tab(void)
  3598. {
  3599. static const gchar *toplvl_domains [] = {
  3600. "museum", "aero",
  3601. "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
  3602. "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
  3603. "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
  3604. "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
  3605. "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
  3606. "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
  3607. "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
  3608. "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
  3609. "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
  3610. "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
  3611. "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
  3612. "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
  3613. "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
  3614. "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
  3615. "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
  3616. "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
  3617. "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
  3618. "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
  3619. "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
  3620. "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
  3621. "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
  3622. "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
  3623. "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
  3624. "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
  3625. "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
  3626. "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
  3627. };
  3628. gint n;
  3629. GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
  3630. cm_return_val_if_fail(htab, NULL);
  3631. for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
  3632. g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
  3633. return htab;
  3634. }
  3635. static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
  3636. {
  3637. const gint MAX_LVL_DOM_NAME_LEN = 6;
  3638. gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
  3639. const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
  3640. register gchar *p;
  3641. if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
  3642. return FALSE;
  3643. for (p = buf; p < m && first < last; *p++ = *first++)
  3644. ;
  3645. *p = 0;
  3646. return g_hash_table_lookup(tab, buf) != NULL;
  3647. }
  3648. /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
  3649. gboolean get_email_part(const gchar *start, const gchar *scanpos,
  3650. const gchar **bp, const gchar **ep, gboolean hdr)
  3651. {
  3652. /* more complex than the uri part because we need to scan back and forward starting from
  3653. * the scan position. */
  3654. gboolean result = FALSE;
  3655. const gchar *bp_ = NULL;
  3656. const gchar *ep_ = NULL;
  3657. static GHashTable *dom_tab;
  3658. const gchar *last_dot = NULL;
  3659. const gchar *prelast_dot = NULL;
  3660. const gchar *last_tld_char = NULL;
  3661. /* the informative part of the email address (describing the name
  3662. * of the email address owner) may contain quoted parts. the
  3663. * closure stack stores the last encountered quotes. */
  3664. gchar closure_stack[128];
  3665. gchar *ptr = closure_stack;
  3666. cm_return_val_if_fail(start != NULL, FALSE);
  3667. cm_return_val_if_fail(scanpos != NULL, FALSE);
  3668. cm_return_val_if_fail(bp != NULL, FALSE);
  3669. cm_return_val_if_fail(ep != NULL, FALSE);
  3670. if (hdr) {
  3671. const gchar *start_quote = NULL;
  3672. const gchar *end_quote = NULL;
  3673. search_again:
  3674. /* go to the real start */
  3675. if (start[0] == ',')
  3676. start++;
  3677. if (start[0] == ';')
  3678. start++;
  3679. while (start[0] == '\n' || start[0] == '\r')
  3680. start++;
  3681. while (start[0] == ' ' || start[0] == '\t')
  3682. start++;
  3683. *bp = start;
  3684. /* check if there are quotes (to skip , in them) */
  3685. if (*start == '"') {
  3686. start_quote = start;
  3687. start++;
  3688. end_quote = strstr(start, "\"");
  3689. } else {
  3690. start_quote = NULL;
  3691. end_quote = NULL;
  3692. }
  3693. /* skip anything between quotes */
  3694. if (start_quote && end_quote) {
  3695. start = end_quote;
  3696. }
  3697. /* find end (either , or ; or end of line) */
  3698. if (strstr(start, ",") && strstr(start, ";"))
  3699. *ep = strstr(start,",") < strstr(start, ";")
  3700. ? strstr(start, ",") : strstr(start, ";");
  3701. else if (strstr(start, ","))
  3702. *ep = strstr(start, ",");
  3703. else if (strstr(start, ";"))
  3704. *ep = strstr(start, ";");
  3705. else
  3706. *ep = start+strlen(start);
  3707. /* go back to real start */
  3708. if (start_quote && end_quote) {
  3709. start = start_quote;
  3710. }
  3711. /* check there's still an @ in that, or search
  3712. * further if possible */
  3713. if (strstr(start, "@") && strstr(start, "@") < *ep)
  3714. return TRUE;
  3715. else if (*ep < start+strlen(start)) {
  3716. start = *ep;
  3717. goto search_again;
  3718. } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
  3719. *bp = start_quote;
  3720. return TRUE;
  3721. } else
  3722. return FALSE;
  3723. }
  3724. if (!dom_tab)
  3725. dom_tab = create_domain_tab();
  3726. cm_return_val_if_fail(dom_tab, FALSE);
  3727. /* scan start of address */
  3728. for (bp_ = scanpos - 1;
  3729. bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
  3730. ;
  3731. /* TODO: should start with an alnum? */
  3732. bp_++;
  3733. for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
  3734. ;
  3735. if (bp_ != scanpos) {
  3736. /* scan end of address */
  3737. for (ep_ = scanpos + 1;
  3738. *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
  3739. if (*ep_ == '.') {
  3740. prelast_dot = last_dot;
  3741. last_dot = ep_;
  3742. if (*(last_dot + 1) == '.') {
  3743. if (prelast_dot == NULL)
  3744. return FALSE;
  3745. last_dot = prelast_dot;
  3746. break;
  3747. }
  3748. }
  3749. /* TODO: really should terminate with an alnum? */
  3750. for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
  3751. --ep_)
  3752. ;
  3753. ep_++;
  3754. if (last_dot == NULL)
  3755. return FALSE;
  3756. if (last_dot >= ep_)
  3757. last_dot = prelast_dot;
  3758. if (last_dot == NULL || (scanpos + 1 >= last_dot))
  3759. return FALSE;
  3760. last_dot++;
  3761. for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
  3762. if (*last_tld_char == '?')
  3763. break;
  3764. if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
  3765. result = TRUE;
  3766. *ep = ep_;
  3767. *bp = bp_;
  3768. }
  3769. if (!result) return FALSE;
  3770. if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
  3771. && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
  3772. && IS_RFC822_CHAR(*(ep_ + 3))) {
  3773. /* this informative part with an @ in it is
  3774. * followed by the email address */
  3775. ep_ += 3;
  3776. /* go to matching '>' (or next non-rfc822 char, like \n) */
  3777. for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
  3778. ;
  3779. /* include the bracket */
  3780. if (*ep_ == '>') ep_++;
  3781. /* include the leading quote */
  3782. bp_--;
  3783. *ep = ep_;
  3784. *bp = bp_;
  3785. return TRUE;
  3786. }
  3787. /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
  3788. if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
  3789. return FALSE;
  3790. /* see if this is <bracketed>; in this case we also scan for the informative part. */
  3791. if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
  3792. return TRUE;
  3793. #define FULL_STACK() ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
  3794. #define IN_STACK() (ptr > closure_stack)
  3795. /* has underrun check */
  3796. #define POP_STACK() if(IN_STACK()) --ptr
  3797. /* has overrun check */
  3798. #define PUSH_STACK(c) if(!FULL_STACK()) *ptr++ = (c); else return TRUE
  3799. /* has underrun check */
  3800. #define PEEK_STACK() (IN_STACK() ? *(ptr - 1) : 0)
  3801. ep_++;
  3802. /* scan for the informative part. */
  3803. for (bp_ -= 2; bp_ >= start; bp_--) {
  3804. /* if closure on the stack keep scanning */
  3805. if (PEEK_STACK() == *bp_) {
  3806. POP_STACK();
  3807. continue;
  3808. }
  3809. if (!IN_STACK() && (*bp_ == '\'' || *bp_ == '"')) {
  3810. PUSH_STACK(*bp_);
  3811. continue;
  3812. }
  3813. /* if nothing in the closure stack, do the special conditions
  3814. * the following if..else expression simply checks whether
  3815. * a token is acceptable. if not acceptable, the clause
  3816. * should terminate the loop with a 'break' */
  3817. if (!PEEK_STACK()) {
  3818. if (*bp_ == '-'
  3819. && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
  3820. && (((bp_ + 1) < ep_) && isalnum(*(bp_ + 1)))) {
  3821. /* hyphens are allowed, but only in
  3822. between alnums */
  3823. } else if (strchr(" \"'", *bp_)) {
  3824. /* but anything not being a punctiation
  3825. is ok */
  3826. } else {
  3827. break; /* anything else is rejected */
  3828. }
  3829. }
  3830. }
  3831. bp_++;
  3832. /* scan forward (should start with an alnum) */
  3833. for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
  3834. ;
  3835. #undef PEEK_STACK
  3836. #undef PUSH_STACK
  3837. #undef POP_STACK
  3838. #undef IN_STACK
  3839. #undef FULL_STACK
  3840. *bp = bp_;
  3841. *ep = ep_;
  3842. return result;
  3843. }
  3844. #undef IS_QUOTE
  3845. #undef IS_ASCII_ALNUM
  3846. #undef IS_RFC822_CHAR
  3847. gchar *make_email_string(const gchar *bp, const gchar *ep)
  3848. {
  3849. /* returns a mailto: URI; mailto: is also used to detect the
  3850. * uri type later on in the button_pressed signal handler */
  3851. gchar *tmp;
  3852. gchar *result;
  3853. tmp = g_strndup(bp, ep - bp);
  3854. result = g_strconcat("mailto:", tmp, NULL);
  3855. g_free(tmp);
  3856. return result;
  3857. }
  3858. gchar *make_http_string(const gchar *bp, const gchar *ep)
  3859. {
  3860. /* returns an http: URI; */
  3861. gchar *tmp;
  3862. gchar *result;
  3863. while (bp && *bp && g_ascii_isspace(*bp))
  3864. bp++;
  3865. tmp = g_strndup(bp, ep - bp);
  3866. result = g_strconcat("http://", tmp, NULL);
  3867. g_free(tmp);
  3868. return result;
  3869. }
  3870. static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
  3871. {
  3872. FILE *fp = g_fopen(path, "rb");
  3873. gchar buf[BUFFSIZE];
  3874. gchar *result = NULL;
  3875. if (!fp)
  3876. return NULL;
  3877. while (fgets(buf, sizeof (buf), fp) != NULL) {
  3878. gchar **parts = g_strsplit(buf, ";", 3);
  3879. gchar *trimmed = parts[0];
  3880. while (trimmed[0] == ' ' || trimmed[0] == '\t')
  3881. trimmed++;
  3882. while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
  3883. trimmed[strlen(trimmed)-1] = '\0';
  3884. if (!strcmp(trimmed, type)) {
  3885. gboolean needsterminal = FALSE;
  3886. if (parts[2] && strstr(parts[2], "needsterminal")) {
  3887. needsterminal = TRUE;
  3888. }
  3889. if (parts[2] && strstr(parts[2], "test=")) {
  3890. gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
  3891. gchar *testcmd = orig_testcmd;
  3892. if (strstr(testcmd,";"))
  3893. *(strstr(testcmd,";")) = '\0';
  3894. while (testcmd[0] == ' ' || testcmd[0] == '\t')
  3895. testcmd++;
  3896. while (testcmd[strlen(testcmd)-1] == '\n')
  3897. testcmd[strlen(testcmd)-1] = '\0';
  3898. while (testcmd[strlen(testcmd)-1] == '\r')
  3899. testcmd[strlen(testcmd)-1] = '\0';
  3900. while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
  3901. testcmd[strlen(testcmd)-1] = '\0';
  3902. if (strstr(testcmd, "%s")) {
  3903. gchar *tmp = g_strdup_printf(testcmd, file_to_open);
  3904. gint res = system(tmp);
  3905. g_free(tmp);
  3906. g_free(orig_testcmd);
  3907. if (res != 0) {
  3908. g_strfreev(parts);
  3909. continue;
  3910. }
  3911. } else {
  3912. gint res = system(testcmd);
  3913. g_free(orig_testcmd);
  3914. if (res != 0) {
  3915. g_strfreev(parts);
  3916. continue;
  3917. }
  3918. }
  3919. }
  3920. trimmed = parts[1];
  3921. while (trimmed[0] == ' ' || trimmed[0] == '\t')
  3922. trimmed++;
  3923. while (trimmed[strlen(trimmed)-1] == '\n')
  3924. trimmed[strlen(trimmed)-1] = '\0';
  3925. while (trimmed[strlen(trimmed)-1] == '\r')
  3926. trimmed[strlen(trimmed)-1] = '\0';
  3927. while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
  3928. trimmed[strlen(trimmed)-1] = '\0';
  3929. result = g_strdup(trimmed);
  3930. g_strfreev(parts);
  3931. fclose(fp);
  3932. /* if there are no single quotes around %s, add them.
  3933. * '.*%s.*' is ok, as in display 'png:%s'
  3934. */
  3935. if (strstr(result, "%s")
  3936. && !(strstr(result, "'") < strstr(result,"%s") &&
  3937. strstr(strstr(result,"%s"), "'"))) {
  3938. gchar *start = g_strdup(result);
  3939. gchar *end = g_strdup(strstr(result, "%s")+2);
  3940. gchar *tmp;
  3941. *strstr(start, "%s") = '\0';
  3942. tmp = g_strconcat(start,"'%s'",end, NULL);
  3943. g_free(start);
  3944. g_free(end);
  3945. g_free(result);
  3946. result = tmp;
  3947. }
  3948. if (needsterminal) {
  3949. gchar *tmp = g_strdup_printf("xterm -e %s", result);
  3950. g_free(result);
  3951. result = tmp;
  3952. }
  3953. return result;
  3954. }
  3955. g_strfreev(parts);
  3956. }
  3957. fclose(fp);
  3958. return NULL;
  3959. }
  3960. gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
  3961. {
  3962. gchar *result = NULL;
  3963. gchar *path = NULL;
  3964. if (type == NULL)
  3965. return NULL;
  3966. path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
  3967. result = mailcap_get_command_in_file(path, type, file_to_open);
  3968. g_free(path);
  3969. if (result)
  3970. return result;
  3971. result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
  3972. return result;
  3973. }
  3974. void mailcap_update_default(const gchar *type, const gchar *command)
  3975. {
  3976. gchar *path = NULL, *outpath = NULL;
  3977. path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
  3978. outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
  3979. FILE *fp = g_fopen(path, "rb");
  3980. FILE *outfp = g_fopen(outpath, "wb");
  3981. gchar buf[BUFFSIZE];
  3982. gboolean err = FALSE;
  3983. if (!outfp) {
  3984. g_free(path);
  3985. g_free(outpath);
  3986. fclose(fp);
  3987. return;
  3988. }
  3989. while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
  3990. gchar **parts = g_strsplit(buf, ";", 3);
  3991. gchar *trimmed = parts[0];
  3992. while (trimmed[0] == ' ')
  3993. trimmed++;
  3994. while (trimmed[strlen(trimmed)-1] == ' ')
  3995. trimmed[strlen(trimmed)-1] = '\0';
  3996. if (!strcmp(trimmed, type)) {
  3997. g_strfreev(parts);
  3998. continue;
  3999. }
  4000. else {
  4001. if(fputs(buf, outfp) == EOF) {
  4002. err = TRUE;
  4003. break;
  4004. }
  4005. }
  4006. g_strfreev(parts);
  4007. }
  4008. if (fprintf(outfp, "%s; %s\n", type, command) < 0)
  4009. err = TRUE;
  4010. if (fp)
  4011. fclose(fp);
  4012. if (fclose(outfp) == EOF)
  4013. err = TRUE;
  4014. if (!err)
  4015. g_rename(outpath, path);
  4016. g_free(path);
  4017. g_free(outpath);
  4018. }
  4019. gint copy_dir(const gchar *src, const gchar *dst)
  4020. {
  4021. GDir *dir;
  4022. const gchar *name;
  4023. if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
  4024. g_warning("failed to open directory: %s\n", src);
  4025. return -1;
  4026. }
  4027. if (make_dir(dst) < 0)
  4028. return -1;
  4029. while ((name = g_dir_read_name(dir)) != NULL) {
  4030. gchar *old_file, *new_file;
  4031. old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
  4032. new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
  4033. debug_print("copying: %s -> %s\n", old_file, new_file);
  4034. if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
  4035. gint r = copy_file(old_file, new_file, TRUE);
  4036. if (r < 0) {
  4037. g_dir_close(dir);
  4038. return r;
  4039. }
  4040. }
  4041. #ifndef G_OS_WIN32
  4042. /* Windows has no symlinks. Or well, Vista seems to
  4043. have something like this but the semantics might be
  4044. different. Thus we don't use it under Windows. */
  4045. else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
  4046. GError *error;
  4047. gint r = 0;
  4048. gchar *target = g_file_read_link(old_file, &error);
  4049. if (target)
  4050. r = symlink(target, new_file);
  4051. g_free(target);
  4052. if (r < 0) {
  4053. g_dir_close(dir);
  4054. return r;
  4055. }
  4056. }
  4057. #endif /*G_OS_WIN32*/
  4058. else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
  4059. gint r = copy_dir(old_file, new_file);
  4060. if (r < 0) {
  4061. g_dir_close(dir);
  4062. return r;
  4063. }
  4064. }
  4065. }
  4066. g_dir_close(dir);
  4067. return 0;
  4068. }
  4069. /* crude test to see if a file is an email. */
  4070. gboolean file_is_email (const gchar *filename)
  4071. {
  4072. FILE *fp = NULL;
  4073. gchar buffer[2048];
  4074. gint i = 0;
  4075. gint score = 0;
  4076. if (filename == NULL)
  4077. return FALSE;
  4078. if ((fp = g_fopen(filename, "rb")) == NULL)
  4079. return FALSE;
  4080. while (i < 60 && score < 3
  4081. && fgets(buffer, sizeof (buffer), fp) > 0) {
  4082. if (!strncmp(buffer, "From:", strlen("From:")))
  4083. score++;
  4084. else if (!strncmp(buffer, "Date:", strlen("Date:")))
  4085. score++;
  4086. else if (!strncmp(buffer, "Message-ID:", strlen("Message-ID:")))
  4087. score++;
  4088. else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
  4089. score++;
  4090. i++;
  4091. }
  4092. fclose(fp);
  4093. return (score >= 3);
  4094. }
  4095. gboolean sc_g_list_bigger(GList *list, gint max)
  4096. {
  4097. GList *cur = list;
  4098. int i = 0;
  4099. while (cur && i <= max+1) {
  4100. i++;
  4101. cur = cur->next;
  4102. }
  4103. return (i > max);
  4104. }
  4105. gboolean sc_g_slist_bigger(GSList *list, gint max)
  4106. {
  4107. GSList *cur = list;
  4108. int i = 0;
  4109. while (cur && i <= max+1) {
  4110. i++;
  4111. cur = cur->next;
  4112. }
  4113. return (i > max);
  4114. }
  4115. const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
  4116. const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
  4117. NULL, NULL, NULL, NULL, NULL, NULL};
  4118. const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
  4119. const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL,
  4120. NULL, NULL, NULL, NULL, NULL, NULL};
  4121. gint daynames_len[] = {0,0,0,0,0,0,0};
  4122. gint monthnames_len[] = {0,0,0,0,0,0,
  4123. 0,0,0,0,0,0};
  4124. gint s_daynames_len[] = {0,0,0,0,0,0,0};
  4125. gint s_monthnames_len[] = {0,0,0,0,0,0,
  4126. 0,0,0,0,0,0};
  4127. const gchar *s_am_up = NULL;
  4128. const gchar *s_pm_up = NULL;
  4129. const gchar *s_am_low = NULL;
  4130. const gchar *s_pm_low = NULL;
  4131. gint s_am_up_len = 0;
  4132. gint s_pm_up_len = 0;
  4133. gint s_am_low_len = 0;
  4134. gint s_pm_low_len = 0;
  4135. const gchar *def_loc_format = NULL;
  4136. const gchar *date_loc_format = NULL;
  4137. const gchar *time_loc_format = NULL;
  4138. const gchar *time_am_pm = NULL;
  4139. static gboolean time_names_init_done = FALSE;
  4140. static void init_time_names(void)
  4141. {
  4142. int i = 0;
  4143. daynames[0] = Q_("Complete day name for use by strftime|Sunday");
  4144. daynames[1] = Q_("Complete day name for use by strftime|Monday");
  4145. daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
  4146. daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
  4147. daynames[4] = Q_("Complete day name for use by strftime|Thursday");
  4148. daynames[5] = Q_("Complete day name for use by strftime|Friday");
  4149. daynames[6] = Q_("Complete day name for use by strftime|Saturday");
  4150. monthnames[0] = Q_("Complete month name for use by strftime|January");
  4151. monthnames[1] = Q_("Complete month name for use by strftime|February");
  4152. monthnames[2] = Q_("Complete month name for use by strftime|March");
  4153. monthnames[3] = Q_("Complete month name for use by strftime|April");
  4154. monthnames[4] = Q_("Complete month name for use by strftime|May");
  4155. monthnames[5] = Q_("Complete month name for use by strftime|June");
  4156. monthnames[6] = Q_("Complete month name for use by strftime|July");
  4157. monthnames[7] = Q_("Complete month name for use by strftime|August");
  4158. monthnames[8] = Q_("Complete month name for use by strftime|September");
  4159. monthnames[9] = Q_("Complete month name for use by strftime|October");
  4160. monthnames[10] = Q_("Complete month name for use by strftime|November");
  4161. monthnames[11] = Q_("Complete month name for use by strftime|December");
  4162. s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
  4163. s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
  4164. s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
  4165. s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
  4166. s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
  4167. s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
  4168. s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
  4169. s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
  4170. s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
  4171. s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
  4172. s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
  4173. s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
  4174. s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
  4175. s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
  4176. s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
  4177. s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
  4178. s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
  4179. s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
  4180. s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
  4181. for (i = 0; i < 7; i++) {
  4182. daynames_len[i] = strlen(daynames[i]);
  4183. s_daynames_len[i] = strlen(s_daynames[i]);
  4184. }
  4185. for (i = 0; i < 12; i++) {
  4186. monthnames_len[i] = strlen(monthnames[i]);
  4187. s_monthnames_len[i] = strlen(s_monthnames[i]);
  4188. }
  4189. s_am_up = Q_("For use by strftime (morning)|AM");
  4190. s_pm_up = Q_("For use by strftime (afternoon)|PM");
  4191. s_am_low = Q_("For use by strftime (morning, lowercase)|am");
  4192. s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
  4193. s_am_up_len = strlen(s_am_up);
  4194. s_pm_up_len = strlen(s_pm_up);
  4195. s_am_low_len = strlen(s_am_low);
  4196. s_pm_low_len = strlen(s_pm_low);
  4197. def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
  4198. date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
  4199. time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
  4200. time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
  4201. time_names_init_done = TRUE;
  4202. }
  4203. #define CHECK_SIZE() { \
  4204. total_done += len; \
  4205. if (total_done >= buflen) { \
  4206. buf[buflen-1] = '\0'; \
  4207. return 0; \
  4208. } \
  4209. }
  4210. size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
  4211. {
  4212. gchar *curpos = buf;
  4213. gint total_done = 0;
  4214. gchar subbuf[64], subfmt[64];
  4215. static time_t last_tzset = (time_t)0;
  4216. if (!time_names_init_done)
  4217. init_time_names();
  4218. if (format == NULL || lt == NULL)
  4219. return 0;
  4220. if (last_tzset != time(NULL)) {
  4221. tzset();
  4222. last_tzset = time(NULL);
  4223. }
  4224. while(*format) {
  4225. if (*format == '%') {
  4226. gint len = 0, tmp = 0;
  4227. format++;
  4228. switch(*format) {
  4229. case '%':
  4230. len = 1; CHECK_SIZE();
  4231. *curpos = '%';
  4232. break;
  4233. case 'a':
  4234. len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
  4235. strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
  4236. break;
  4237. case 'A':
  4238. len = daynames_len[lt->tm_wday]; CHECK_SIZE();
  4239. strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
  4240. break;
  4241. case 'b':
  4242. case 'h':
  4243. len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
  4244. strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
  4245. break;
  4246. case 'B':
  4247. len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
  4248. strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
  4249. break;
  4250. case 'c':
  4251. fast_strftime(subbuf, 64, def_loc_format, lt);
  4252. len = strlen(subbuf); CHECK_SIZE();
  4253. strncpy2(curpos, subbuf, buflen - total_done);
  4254. break;
  4255. case 'C':
  4256. total_done += 2; CHECK_SIZE();
  4257. tmp = (lt->tm_year + 1900)/100;
  4258. *curpos++ = '0'+(tmp / 10);
  4259. *curpos++ = '0'+(tmp % 10);
  4260. break;
  4261. case 'd':
  4262. total_done += 2; CHECK_SIZE();
  4263. *curpos++ = '0'+(lt->tm_mday / 10);
  4264. *curpos++ = '0'+(lt->tm_mday % 10);
  4265. break;
  4266. case 'D':
  4267. total_done += 8; CHECK_SIZE();
  4268. *curpos++ = '0'+((lt->tm_mon+1) / 10);
  4269. *curpos++ = '0'+((lt->tm_mon+1) % 10);
  4270. *curpos++ = '/';
  4271. *curpos++ = '0'+(lt->tm_mday / 10);
  4272. *curpos++ = '0'+(lt->tm_mday % 10);
  4273. *curpos++ = '/';
  4274. tmp = lt->tm_year%100;
  4275. *curpos++ = '0'+(tmp / 10);
  4276. *curpos++ = '0'+(tmp % 10);
  4277. break;
  4278. case 'e':
  4279. len = 2; CHECK_SIZE();
  4280. snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
  4281. break;
  4282. case 'F':
  4283. len = 10; CHECK_SIZE();
  4284. snprintf(curpos, buflen - total_done, "%4d-%02d-%02d",
  4285. lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
  4286. break;
  4287. case 'H':
  4288. total_done += 2; CHECK_SIZE();
  4289. *curpos++ = '0'+(lt->tm_hour / 10);
  4290. *curpos++ = '0'+(lt->tm_hour % 10);
  4291. break;
  4292. case 'I':
  4293. total_done += 2; CHECK_SIZE();
  4294. tmp = lt->tm_hour;
  4295. if (tmp > 12)
  4296. tmp -= 12;
  4297. else if (tmp == 0)
  4298. tmp = 12;
  4299. *curpos++ = '0'+(tmp / 10);
  4300. *curpos++ = '0'+(tmp % 10);
  4301. break;
  4302. case 'j':
  4303. len = 3; CHECK_SIZE();
  4304. snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
  4305. break;
  4306. case 'k':
  4307. len = 2; CHECK_SIZE();
  4308. snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
  4309. break;
  4310. case 'l':
  4311. len = 2; CHECK_SIZE();
  4312. tmp = lt->tm_hour;
  4313. if (tmp > 12)
  4314. tmp -= 12;
  4315. else if (tmp == 0)
  4316. tmp = 12;
  4317. snprintf(curpos, buflen - total_done, "%2d", tmp);
  4318. break;
  4319. case 'm':
  4320. total_done += 2; CHECK_SIZE();
  4321. tmp = lt->tm_mon + 1;
  4322. *curpos++ = '0'+(tmp / 10);
  4323. *curpos++ = '0'+(tmp % 10);
  4324. break;
  4325. case 'M':
  4326. total_done += 2; CHECK_SIZE();
  4327. *curpos++ = '0'+(lt->tm_min / 10);
  4328. *curpos++ = '0'+(lt->tm_min % 10);
  4329. break;
  4330. case 'n':
  4331. len = 1; CHECK_SIZE();
  4332. *curpos = '\n';
  4333. break;
  4334. case 'p':
  4335. if (lt->tm_hour >= 12) {
  4336. len = s_pm_up_len; CHECK_SIZE();
  4337. snprintf(curpos, buflen-total_done, "%s", s_pm_up);
  4338. } else {
  4339. len = s_am_up_len; CHECK_SIZE();
  4340. snprintf(curpos, buflen-total_done, "%s", s_am_up);
  4341. }
  4342. break;
  4343. case 'P':
  4344. if (lt->tm_hour >= 12) {
  4345. len = s_pm_low_len; CHECK_SIZE();
  4346. snprintf(curpos, buflen-total_done, "%s", s_pm_low);
  4347. } else {
  4348. len = s_am_low_len; CHECK_SIZE();
  4349. snprintf(curpos, buflen-total_done, "%s", s_am_low);
  4350. }
  4351. break;
  4352. case 'r':
  4353. fast_strftime(subbuf, 64, time_am_pm, lt);
  4354. len = strlen(subbuf); CHECK_SIZE();
  4355. strncpy2(curpos, subbuf, buflen - total_done);
  4356. break;
  4357. case 'R':
  4358. total_done += 5; CHECK_SIZE();
  4359. *curpos++ = '0'+(lt->tm_hour / 10);
  4360. *curpos++ = '0'+(lt->tm_hour % 10);
  4361. *curpos++ = ':';
  4362. *curpos++ = '0'+(lt->tm_min / 10);
  4363. *curpos++ = '0'+(lt->tm_min % 10);
  4364. break;
  4365. case 's':
  4366. snprintf(subbuf, 64, "%ld", mktime(lt));
  4367. len = strlen(subbuf); CHECK_SIZE();
  4368. strncpy2(curpos, subbuf, buflen - total_done);
  4369. break;
  4370. case 'S':
  4371. total_done += 2; CHECK_SIZE();
  4372. *curpos++ = '0'+(lt->tm_sec / 10);
  4373. *curpos++ = '0'+(lt->tm_sec % 10);
  4374. break;
  4375. case 't':
  4376. len = 1; CHECK_SIZE();
  4377. *curpos = '\t';
  4378. break;
  4379. case 'T':
  4380. total_done += 8; CHECK_SIZE();
  4381. *curpos++ = '0'+(lt->tm_hour / 10);
  4382. *curpos++ = '0'+(lt->tm_hour % 10);
  4383. *curpos++ = ':';
  4384. *curpos++ = '0'+(lt->tm_min / 10);
  4385. *curpos++ = '0'+(lt->tm_min % 10);
  4386. *curpos++ = ':';
  4387. *curpos++ = '0'+(lt->tm_sec / 10);
  4388. *curpos++ = '0'+(lt->tm_sec % 10);
  4389. break;
  4390. case 'u':
  4391. len = 1; CHECK_SIZE();
  4392. snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
  4393. break;
  4394. case 'w':
  4395. len = 1; CHECK_SIZE();
  4396. snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
  4397. break;
  4398. case 'x':
  4399. fast_strftime(subbuf, 64, date_loc_format, lt);
  4400. len = strlen(subbuf); CHECK_SIZE();
  4401. strncpy2(curpos, subbuf, buflen - total_done);
  4402. break;
  4403. case 'X':
  4404. fast_strftime(subbuf, 64, time_loc_format, lt);
  4405. len = strlen(subbuf); CHECK_SIZE();
  4406. strncpy2(curpos, subbuf, buflen - total_done);
  4407. break;
  4408. case 'y':
  4409. total_done += 2; CHECK_SIZE();
  4410. tmp = lt->tm_year%100;
  4411. *curpos++ = '0'+(tmp / 10);
  4412. *curpos++ = '0'+(tmp % 10);
  4413. break;
  4414. case 'Y':
  4415. len = 4; CHECK_SIZE();
  4416. snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
  4417. break;
  4418. case 'G':
  4419. case 'g':
  4420. case 'U':
  4421. case 'V':
  4422. case 'W':
  4423. case 'z':
  4424. case 'Z':
  4425. case '+':
  4426. /* let these complicated ones be done with the libc */
  4427. snprintf(subfmt, 64, "%%%c", *format);
  4428. strftime(subbuf, 64, subfmt, lt);
  4429. len = strlen(subbuf); CHECK_SIZE();
  4430. strncpy2(curpos, subbuf, buflen - total_done);
  4431. break;
  4432. case 'E':
  4433. case 'O':
  4434. /* let these complicated modifiers be done with the libc */
  4435. snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
  4436. strftime(subbuf, 64, subfmt, lt);
  4437. len = strlen(subbuf); CHECK_SIZE();
  4438. strncpy2(curpos, subbuf, buflen - total_done);
  4439. format++;
  4440. break;
  4441. default:
  4442. if (format && *format)
  4443. g_warning("format error (%c)", *format);
  4444. *curpos = '\0';
  4445. return total_done;
  4446. }
  4447. curpos += len;
  4448. format++;
  4449. } else {
  4450. int len = 1; CHECK_SIZE();
  4451. *curpos++ = *format++;
  4452. }
  4453. }
  4454. *curpos++ = '\0';
  4455. return total_done;
  4456. }
  4457. gboolean prefs_common_get_use_shred(void);
  4458. #ifdef G_OS_WIN32
  4459. #define WEXITSTATUS(x) (x)
  4460. #endif
  4461. int claws_unlink(const gchar *filename)
  4462. {
  4463. struct stat s;
  4464. static int found_shred = -1;
  4465. static const gchar *args[4];
  4466. if (filename == NULL)
  4467. return 0;
  4468. if (prefs_common_get_use_shred()) {
  4469. if (found_shred == -1) {
  4470. /* init */
  4471. args[0] = g_find_program_in_path("shred");
  4472. debug_print("found shred: %s\n", args[0]);
  4473. found_shred = (args[0] != NULL) ? 1:0;
  4474. args[1] = "-f";
  4475. args[3] = NULL;
  4476. }
  4477. if (found_shred == 1) {
  4478. if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
  4479. if (s.st_nlink == 1) {
  4480. gint status=0;
  4481. args[2] = filename;
  4482. g_spawn_sync(NULL, (gchar **)args, NULL, 0,
  4483. NULL, NULL, NULL, NULL, &status, NULL);
  4484. debug_print("%s %s exited with status %d\n",
  4485. args[0], filename, WEXITSTATUS(status));
  4486. if (truncate(filename, 0) < 0)
  4487. g_warning("couln't truncate");
  4488. }
  4489. }
  4490. }
  4491. }
  4492. return g_unlink(filename);
  4493. }