Statistics
| Revision:

root / libsylph / utils.c @ 705

History | View | Annotate | Download (68.8 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library 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 GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include "defs.h"
25
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <ctype.h>
31
#include <errno.h>
32
#include <stdlib.h>
33
#include <sys/stat.h>
34
#include <fcntl.h>
35
#include <unistd.h>
36
#include <stdarg.h>
37
#include <sys/types.h>
38
#if HAVE_SYS_WAIT_H
39
#  include <sys/wait.h>
40
#endif
41
#include <dirent.h>
42
#include <time.h>
43
44
#ifdef G_OS_WIN32
45
#  include <windows.h>
46
#  include <wchar.h>
47
#  include <direct.h>
48
#  include <io.h>
49
#endif
50
51
#include "utils.h"
52
#include "socket.h"
53
54
#define BUFFSIZE        8192
55
56
static gboolean debug_mode = FALSE;
57
58
59
#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
60
gint g_chdir(const gchar *path)
61
{
62
#ifdef G_OS_WIN32
63
        if (G_WIN32_HAVE_WIDECHAR_API()) {
64
                wchar_t *wpath;
65
                gint retval;
66
                gint save_errno;
67
68
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
69
                if (wpath == NULL) {
70
                        errno = EINVAL;
71
                        return -1;
72
                }
73
74
                retval = _wchdir(wpath);
75
                save_errno = errno;
76
77
                g_free(wpath);
78
79
                errno = save_errno;
80
                return retval;
81
        } else {
82
                gchar *cp_path;
83
                gint retval;
84
                gint save_errno;
85
86
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
87
                if (cp_path == NULL) {
88
                        errno = EINVAL;
89
                        return -1;
90
                }
91
92
                retval = chdir(cp_path);
93
                save_errno = errno;
94
95
                g_free(cp_path);
96
97
                errno = save_errno;
98
                return retval;
99
        }
100
#else
101
        return chdir(path);
102
#endif
103
}
104
105
gint g_chmod(const gchar *path, gint mode)
106
{
107
#ifdef G_OS_WIN32
108
        if (G_WIN32_HAVE_WIDECHAR_API()) {
109
                wchar_t *wpath;
110
                gint retval;
111
                gint save_errno;
112
113
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
114
                if (wpath == NULL) {
115
                        errno = EINVAL;
116
                        return -1;
117
                }
118
119
                retval = _wchmod(wpath, mode);
120
                save_errno = errno;
121
122
                g_free(wpath);
123
124
                errno = save_errno;
125
                return retval;
126
        } else {
127
                gchar *cp_path;
128
                gint retval;
129
                gint save_errno;
130
131
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
132
                if (cp_path == NULL) {
133
                        errno = EINVAL;
134
                        return -1;
135
                }
136
137
                retval = chmod(cp_path, mode);
138
                save_errno = errno;
139
140
                g_free(cp_path);
141
142
                errno = save_errno;
143
                return retval;
144
        }
145
#else
146
        return chmod(path, mode);
147
#endif
148
}
149
#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
150
151
void list_free_strings(GList *list)
152
{
153
        list = g_list_first(list);
154
155
        while (list != NULL) {
156
                g_free(list->data);
157
                list = list->next;
158
        }
159
}
160
161
void slist_free_strings(GSList *list)
162
{
163
        while (list != NULL) {
164
                g_free(list->data);
165
                list = list->next;
166
        }
167
}
168
169
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
170
{
171
        g_free(key);
172
}
173
174
void hash_free_strings(GHashTable *table)
175
{
176
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
177
}
178
179
static void hash_free_value_mem_func(gpointer key, gpointer value,
180
                                     gpointer data)
181
{
182
        g_free(value);
183
}
184
185
void hash_free_value_mem(GHashTable *table)
186
{
187
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
188
}
189
190
gint str_case_equal(gconstpointer v, gconstpointer v2)
191
{
192
        return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
193
}
194
195
guint str_case_hash(gconstpointer key)
196
{
197
        const gchar *p = key;
198
        guint h = *p;
199
200
        if (h) {
201
                h = g_ascii_tolower(h);
202
                for (p += 1; *p != '\0'; p++)
203
                        h = (h << 5) - h + g_ascii_tolower(*p);
204
        }
205
206
        return h;
207
}
208
209
void ptr_array_free_strings(GPtrArray *array)
210
{
211
        gint i;
212
        gchar *str;
213
214
        g_return_if_fail(array != NULL);
215
216
        for (i = 0; i < array->len; i++) {
217
                str = g_ptr_array_index(array, i);
218
                g_free(str);
219
        }
220
}
221
222
gboolean str_find(const gchar *haystack, const gchar *needle)
223
{
224
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
225
}
226
227
gboolean str_case_find(const gchar *haystack, const gchar *needle)
228
{
229
        return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
230
}
231
232
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
233
{
234
        return strcmp(haystack, needle) == 0;
235
}
236
237
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
238
{
239
        return g_ascii_strcasecmp(haystack, needle) == 0;
240
}
241
242
gint to_number(const gchar *nstr)
243
{
244
        register const gchar *p;
245
246
        if (*nstr == '\0') return -1;
247
248
        for (p = nstr; *p != '\0'; p++)
249
                if (!g_ascii_isdigit(*p)) return -1;
250
251
        return atoi(nstr);
252
}
253
254
/* convert integer into string,
255
   nstr must be not lower than 11 characters length */
256
gchar *itos_buf(gchar *nstr, gint n)
257
{
258
        g_snprintf(nstr, 11, "%d", n);
259
        return nstr;
260
}
261
262
/* convert integer into string */
263
gchar *itos(gint n)
264
{
265
        static gchar nstr[11];
266
267
        return itos_buf(nstr, n);
268
}
269
270
gchar *to_human_readable(off_t size)
271
{
272
        static gchar str[10];
273
274
        if (size < 1024)
275
                g_snprintf(str, sizeof(str), _("%dB"), (gint)size);
276
        else if (size >> 10 < 1024)
277
                g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10));
278
        else if (size >> 20 < 1024)
279
                g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20));
280
        else
281
                g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30));
282
283
        return str;
284
}
285
286
/* strcmp with NULL-checking */
287
gint strcmp2(const gchar *s1, const gchar *s2)
288
{
289
        if (s1 == NULL || s2 == NULL)
290
                return -1;
291
        else
292
                return strcmp(s1, s2);
293
}
294
295
/* compare paths */
296
gint path_cmp(const gchar *s1, const gchar *s2)
297
{
298
        gint len1, len2;
299
#ifdef G_OS_WIN32
300
        gchar *s1_, *s2_;
301
#endif
302
303
        if (s1 == NULL || s2 == NULL) return -1;
304
        if (*s1 == '\0' || *s2 == '\0') return -1;
305
306
        len1 = strlen(s1);
307
        len2 = strlen(s2);
308
309
#ifdef G_OS_WIN32
310
        Xstrdup_a(s1_, s1, return -1);
311
        Xstrdup_a(s2_, s2, return -1);
312
        subst_char(s1_, '/', G_DIR_SEPARATOR);
313
        subst_char(s2_, '/', G_DIR_SEPARATOR);
314
        if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--;
315
        if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--;
316
317
        return strncmp(s1_, s2_, MAX(len1, len2));
318
#else
319
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
320
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
321
322
        return strncmp(s1, s2, MAX(len1, len2));
323
#endif
324
}
325
326
/* remove trailing return code */
327
gchar *strretchomp(gchar *str)
328
{
329
        register gchar *s;
330
331
        if (!*str) return str;
332
333
        for (s = str + strlen(str) - 1;
334
             s >= str && (*s == '\n' || *s == '\r');
335
             s--)
336
                *s = '\0';
337
338
        return str;
339
}
340
341
/* remove trailing character */
342
gchar *strtailchomp(gchar *str, gchar tail_char)
343
{
344
        register gchar *s;
345
346
        if (!*str) return str;
347
        if (tail_char == '\0') return str;
348
349
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
350
                *s = '\0';
351
352
        return str;
353
}
354
355
/* remove CR (carriage return) */
356
gchar *strcrchomp(gchar *str)
357
{
358
        register gchar *s;
359
360
        if (!*str) return str;
361
362
        s = str + strlen(str) - 1;
363
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
364
                *(s - 1) = '\n';
365
                *s = '\0';
366
        }
367
368
        return str;
369
}
370
371
/* Similar to `strstr' but this function ignores the case of both strings.  */
372
gchar *strcasestr(const gchar *haystack, const gchar *needle)
373
{
374
        register size_t haystack_len, needle_len;
375
376
        haystack_len = strlen(haystack);
377
        needle_len   = strlen(needle);
378
379
        if (haystack_len < needle_len || needle_len == 0)
380
                return NULL;
381
382
        while (haystack_len >= needle_len) {
383
                if (!g_ascii_strncasecmp(haystack, needle, needle_len))
384
                        return (gchar *)haystack;
385
                else {
386
                        haystack++;
387
                        haystack_len--;
388
                }
389
        }
390
391
        return NULL;
392
}
393
394
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
395
                   gconstpointer needle, size_t needlelen)
396
{
397
        const gchar *haystack_ = (const gchar *)haystack;
398
        const gchar *needle_ = (const gchar *)needle;
399
        const gchar *haystack_cur = (const gchar *)haystack;
400
401
        if (needlelen == 1)
402
                return memchr(haystack_, *needle_, haystacklen);
403
404
        while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
405
               != NULL) {
406
                if (haystacklen - (haystack_cur - haystack_) < needlelen)
407
                        break;
408
                if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
409
                        return (gpointer)haystack_cur;
410
                else
411
                        haystack_cur++;
412
        }
413
414
        return NULL;
415
}
416
417
/* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
418
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
419
{
420
        register const gchar *s = src;
421
        register gchar *d = dest;
422
423
        while (--n && *s)
424
                *d++ = *s++;
425
        *d = '\0';
426
427
        return dest;
428
}
429
430
/* Similar to g_str_has_suffix() but case-insensitive */
431
gboolean str_has_suffix_case(const gchar *str, const gchar *suffix)
432
{
433
        size_t len, s_len;
434
435
        if (!str || !suffix)
436
                return FALSE;
437
438
        len = strlen(str);
439
        s_len = strlen(suffix);
440
441
        if (s_len > len)
442
                return FALSE;
443
444
        return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0);
445
}
446
447
/* Examine if next block is non-ASCII string */
448
gboolean is_next_nonascii(const gchar *s)
449
{
450
        const gchar *p;
451
452
        /* skip head space */
453
        for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
454
                ;
455
        for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
456
                if (*(guchar *)p > 127 || *(guchar *)p < 32)
457
                        return TRUE;
458
        }
459
460
        return FALSE;
461
}
462
463
gint get_next_word_len(const gchar *s)
464
{
465
        gint len = 0;
466
467
        for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
468
                ;
469
470
        return len;
471
}
472
473
/* compare subjects */
474
gint subject_compare(const gchar *s1, const gchar *s2)
475
{
476
        gchar *str1, *str2;
477
478
        if (!s1 || !s2) return -1;
479
        if (!*s1 || !*s2) return -1;
480
481
        Xstrdup_a(str1, s1, return -1);
482
        Xstrdup_a(str2, s2, return -1);
483
484
        trim_subject_for_compare(str1);
485
        trim_subject_for_compare(str2);
486
487
        if (!*str1 || !*str2) return -1;
488
489
        return strcmp(str1, str2);
490
}
491
492
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
493
{
494
        gchar *str1, *str2;
495
496
        if (!s1 || !s2) return -1;
497
498
        Xstrdup_a(str1, s1, return -1);
499
        Xstrdup_a(str2, s2, return -1);
500
501
        trim_subject_for_sort(str1);
502
        trim_subject_for_sort(str2);
503
504
        return g_ascii_strcasecmp(str1, str2);
505
}
506
507
void trim_subject_for_compare(gchar *str)
508
{
509
        gchar *srcp;
510
511
        eliminate_parenthesis(str, '[', ']');
512
        eliminate_parenthesis(str, '(', ')');
513
        g_strstrip(str);
514
515
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
516
                srcp = str + 3;
517
                while (g_ascii_isspace(*srcp)) srcp++;
518
                memmove(str, srcp, strlen(srcp) + 1);
519
        }
520
}
521
522
void trim_subject_for_sort(gchar *str)
523
{
524
        gchar *srcp;
525
526
        g_strstrip(str);
527
528
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
529
                srcp = str + 3;
530
                while (g_ascii_isspace(*srcp)) srcp++;
531
                memmove(str, srcp, strlen(srcp) + 1);
532
        }
533
}
534
535
void trim_subject(gchar *str)
536
{
537
        register gchar *srcp, *destp;
538
        gchar op, cl;
539
        gint in_brace;
540
541
        destp = str;
542
        while (!g_ascii_strncasecmp(destp, "Re:", 3)) {
543
                destp += 3;
544
                while (g_ascii_isspace(*destp)) destp++;
545
        }
546
547
        if (*destp == '[') {
548
                op = '[';
549
                cl = ']';
550
        } else if (*destp == '(') {
551
                op = '(';
552
                cl = ')';
553
        } else
554
                return;
555
556
        srcp = destp + 1;
557
        in_brace = 1;
558
        while (*srcp) {
559
                if (*srcp == op)
560
                        in_brace++;
561
                else if (*srcp == cl)
562
                        in_brace--;
563
                srcp++;
564
                if (in_brace == 0)
565
                        break;
566
        }
567
        while (g_ascii_isspace(*srcp)) srcp++;
568
        memmove(destp, srcp, strlen(srcp) + 1);
569
}
570
571
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
572
{
573
        register gchar *srcp, *destp;
574
        gint in_brace;
575
576
        srcp = destp = str;
577
578
        while ((destp = strchr(destp, op))) {
579
                in_brace = 1;
580
                srcp = destp + 1;
581
                while (*srcp) {
582
                        if (*srcp == op)
583
                                in_brace++;
584
                        else if (*srcp == cl)
585
                                in_brace--;
586
                        srcp++;
587
                        if (in_brace == 0)
588
                                break;
589
                }
590
                while (g_ascii_isspace(*srcp)) srcp++;
591
                memmove(destp, srcp, strlen(srcp) + 1);
592
        }
593
}
594
595
void extract_parenthesis(gchar *str, gchar op, gchar cl)
596
{
597
        register gchar *srcp, *destp;
598
        gint in_brace;
599
600
        srcp = destp = str;
601
602
        while ((srcp = strchr(destp, op))) {
603
                if (destp > str)
604
                        *destp++ = ' ';
605
                memmove(destp, srcp + 1, strlen(srcp));
606
                in_brace = 1;
607
                while(*destp) {
608
                        if (*destp == op)
609
                                in_brace++;
610
                        else if (*destp == cl)
611
                                in_brace--;
612
613
                        if (in_brace == 0)
614
                                break;
615
616
                        destp++;
617
                }
618
        }
619
        *destp = '\0';
620
}
621
622
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
623
                                         gchar op, gchar cl)
624
{
625
        register gchar *srcp, *destp;
626
        gint in_brace;
627
        gboolean in_quote = FALSE;
628
629
        srcp = destp = str;
630
631
        while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
632
                if (destp > str)
633
                        *destp++ = ' ';
634
                memmove(destp, srcp + 1, strlen(srcp));
635
                in_brace = 1;
636
                while(*destp) {
637
                        if (*destp == op && !in_quote)
638
                                in_brace++;
639
                        else if (*destp == cl && !in_quote)
640
                                in_brace--;
641
                        else if (*destp == quote_chr)
642
                                in_quote ^= TRUE;
643
644
                        if (in_brace == 0)
645
                                break;
646
647
                        destp++;
648
                }
649
        }
650
        *destp = '\0';
651
}
652
653
void eliminate_quote(gchar *str, gchar quote_chr)
654
{
655
        register gchar *srcp, *destp;
656
657
        srcp = destp = str;
658
659
        while ((destp = strchr(destp, quote_chr))) {
660
                if ((srcp = strchr(destp + 1, quote_chr))) {
661
                        srcp++;
662
                        while (g_ascii_isspace(*srcp)) srcp++;
663
                        memmove(destp, srcp, strlen(srcp) + 1);
664
                } else {
665
                        *destp = '\0';
666
                        break;
667
                }
668
        }
669
}
670
671
void extract_quote(gchar *str, gchar quote_chr)
672
{
673
        register gchar *p;
674
675
        if ((str = strchr(str, quote_chr))) {
676
                if ((p = strchr(str + 1, quote_chr))) {
677
                        *p = '\0';
678
                        memmove(str, str + 1, p - str);
679
                }
680
        }
681
}
682
683
void eliminate_address_comment(gchar *str)
684
{
685
        register gchar *srcp, *destp;
686
        gint in_brace;
687
688
        srcp = destp = str;
689
690
        while ((destp = strchr(destp, '"'))) {
691
                if ((srcp = strchr(destp + 1, '"'))) {
692
                        srcp++;
693
                        if (*srcp == '@') {
694
                                destp = srcp + 1;
695
                        } else {
696
                                while (g_ascii_isspace(*srcp)) srcp++;
697
                                memmove(destp, srcp, strlen(srcp) + 1);
698
                        }
699
                } else {
700
                        *destp = '\0';
701
                        break;
702
                }
703
        }
704
705
        srcp = destp = str;
706
707
        while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
708
                in_brace = 1;
709
                srcp = destp + 1;
710
                while (*srcp) {
711
                        if (*srcp == '(')
712
                                in_brace++;
713
                        else if (*srcp == ')')
714
                                in_brace--;
715
                        srcp++;
716
                        if (in_brace == 0)
717
                                break;
718
                }
719
                while (g_ascii_isspace(*srcp)) srcp++;
720
                memmove(destp, srcp, strlen(srcp) + 1);
721
        }
722
}
723
724
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
725
{
726
        gboolean in_quote = FALSE;
727
728
        while (*str) {
729
                if (*str == c && !in_quote)
730
                        return (gchar *)str;
731
                if (*str == quote_chr)
732
                        in_quote ^= TRUE;
733
                str++;
734
        }
735
736
        return NULL;
737
}
738
739
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
740
{
741
        gboolean in_quote = FALSE;
742
        const gchar *p;
743
744
        p = str + strlen(str) - 1;
745
        while (p >= str) {
746
                if (*p == c && !in_quote)
747
                        return (gchar *)p;
748
                if (*p == quote_chr)
749
                        in_quote ^= TRUE;
750
                p--;
751
        }
752
753
        return NULL;
754
}
755
756
void extract_address(gchar *str)
757
{
758
        eliminate_address_comment(str);
759
        if (strchr_with_skip_quote(str, '"', '<'))
760
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
761
        g_strstrip(str);
762
}
763
764
void extract_list_id_str(gchar *str)
765
{
766
        if (strchr_with_skip_quote(str, '"', '<'))
767
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
768
        g_strstrip(str);
769
}
770
771
gchar *normalize_address_field(const gchar *str)
772
{
773
        GString *new_str;
774
        GSList *addr_list, *cur;
775
        gchar *addr, *p, *q, *r;
776
        gchar *ret_str;
777
778
        addr_list = address_list_append_orig(NULL, str);
779
780
        new_str = g_string_new(NULL);
781
782
        for (cur = addr_list; cur != NULL; cur = cur->next) {
783
                p = addr = (gchar *)cur->data;
784
                q = strchr_with_skip_quote(p, '"', '<');
785
                if (q && q > p) {
786
                        r = q - 1;
787
                        while (r > p && g_ascii_isspace(*r))
788
                                --r;
789
                        g_string_append_len(new_str, p, r - p + 1);
790
                        g_string_append_c(new_str, ' ');
791
                        p = q;
792
                }
793
                if (*p == '<') {
794
                        q = strchr(p, '>');
795
                        if (q) {
796
                                r = q + 1;
797
                                if (*r) {
798
                                        while (g_ascii_isspace(*r))
799
                                                ++r;
800
                                        g_string_append(new_str, r);
801
                                        if (new_str->len > 0 &&
802
                                            !g_ascii_isspace
803
                                                (new_str->str[new_str->len - 1]))
804
                                                g_string_append_c(new_str, ' ');
805
                                }
806
                                g_string_append_len(new_str, p, q - p + 1);
807
                        } else {
808
                                g_string_append(new_str, p);
809
                                g_string_append_c(new_str, '>');
810
                        }
811
                } else
812
                        g_string_append(new_str, p);
813
814
                if (cur->next)
815
                        g_string_append(new_str, ", ");
816
        }
817
818
        slist_free_strings(addr_list);
819
        ret_str = new_str->str;
820
        g_string_free(new_str, FALSE);
821
822
        return ret_str;
823
}
824
825
gboolean address_equal(const gchar *addr1, const gchar *addr2)
826
{
827
        gchar *addr1_, *addr2_;
828
829
        if (!addr1 || !addr2)
830
                return FALSE;
831
832
        Xstrdup_a(addr1_, addr1, return FALSE);
833
        Xstrdup_a(addr2_, addr2, return FALSE);
834
835
        extract_address(addr1_);
836
        extract_address(addr2_);
837
838
        return strcmp(addr1_, addr2_) == 0;
839
}
840
841
GSList *address_list_append_orig(GSList *addr_list, const gchar *str)
842
{
843
        const gchar *p = str, *q;
844
        gchar *addr;
845
846
        if (!str) return addr_list;
847
848
        while (*p) {
849
                if (*p == ',' || g_ascii_isspace(*p)) {
850
                        ++p;
851
                } else if ((q = strchr_with_skip_quote(p, '"', ','))) {
852
                        addr = g_strndup(p, q - p);
853
                        g_strstrip(addr);
854
                        addr_list = g_slist_append(addr_list, addr);
855
                        p = q + 1;
856
                } else {
857
                        addr = g_strdup(p);
858
                        g_strstrip(addr);
859
                        addr_list = g_slist_append(addr_list, addr);
860
                        break;
861
                }
862
        }
863
864
        return addr_list;
865
}
866
867
GSList *address_list_append(GSList *addr_list, const gchar *str)
868
{
869
        gchar *work;
870
        gchar *workp;
871
872
        if (!str) return addr_list;
873
874
        Xstrdup_a(work, str, return addr_list);
875
876
        eliminate_address_comment(work);
877
        workp = work;
878
879
        while (workp && *workp) {
880
                gchar *p, *next;
881
882
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
883
                        *p = '\0';
884
                        next = p + 1;
885
                } else
886
                        next = NULL;
887
888
                if (strchr_with_skip_quote(workp, '"', '<'))
889
                        extract_parenthesis_with_skip_quote
890
                                (workp, '"', '<', '>');
891
892
                g_strstrip(workp);
893
                if (*workp)
894
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
895
896
                workp = next;
897
        }
898
899
        return addr_list;
900
}
901
902
GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
903
{
904
        const gchar *strp;
905
906
        if (!str) return msgid_list;
907
        strp = str;
908
909
        while (strp && *strp) {
910
                const gchar *start, *end;
911
                gchar *msgid;
912
913
                if ((start = strchr(strp, '<')) != NULL) {
914
                        end = strchr(start + 1, '>');
915
                        if (!end) break;
916
                } else
917
                        break;
918
919
                msgid = g_strndup(start + 1, end - start - 1);
920
                g_strstrip(msgid);
921
                if (*msgid)
922
                        msgid_list = g_slist_prepend(msgid_list, msgid);
923
                else
924
                        g_free(msgid);
925
926
                strp = end + 1;
927
        }
928
929
        return msgid_list;
930
}
931
932
GSList *references_list_append(GSList *msgid_list, const gchar *str)
933
{
934
        GSList *list;
935
936
        list = references_list_prepend(NULL, str);
937
        list = g_slist_reverse(list);
938
        msgid_list = g_slist_concat(msgid_list, list);
939
940
        return msgid_list;
941
}
942
943
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
944
{
945
        gchar *work;
946
        gchar *workp;
947
948
        if (!str) return group_list;
949
950
        Xstrdup_a(work, str, return group_list);
951
952
        workp = work;
953
954
        while (workp && *workp) {
955
                gchar *p, *next;
956
957
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
958
                        *p = '\0';
959
                        next = p + 1;
960
                } else
961
                        next = NULL;
962
963
                g_strstrip(workp);
964
                if (*workp)
965
                        group_list = g_slist_append(group_list,
966
                                                    g_strdup(workp));
967
968
                workp = next;
969
        }
970
971
        return group_list;
972
}
973
974
GList *add_history(GList *list, const gchar *str)
975
{
976
        GList *old;
977
978
        g_return_val_if_fail(str != NULL, list);
979
980
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
981
        if (old) {
982
                g_free(old->data);
983
                list = g_list_remove(list, old->data);
984
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
985
                GList *last;
986
987
                last = g_list_last(list);
988
                if (last) {
989
                        g_free(last->data);
990
                        g_list_remove(list, last->data);
991
                }
992
        }
993
994
        list = g_list_prepend(list, g_strdup(str));
995
996
        return list;
997
}
998
999
void remove_return(gchar *str)
1000
{
1001
        register gchar *p = str;
1002
1003
        while (*p) {
1004
                if (*p == '\n' || *p == '\r')
1005
                        memmove(p, p + 1, strlen(p));
1006
                else
1007
                        p++;
1008
        }
1009
}
1010
1011
void remove_space(gchar *str)
1012
{
1013
        register gchar *p = str;
1014
        register gint spc;
1015
1016
        while (*p) {
1017
                spc = 0;
1018
                while (g_ascii_isspace(*(p + spc)))
1019
                        spc++;
1020
                if (spc)
1021
                        memmove(p, p + spc, strlen(p + spc) + 1);
1022
                else
1023
                        p++;
1024
        }
1025
}
1026
1027
void unfold_line(gchar *str)
1028
{
1029
        register gchar *p = str;
1030
        register gint spc;
1031
1032
        while (*p) {
1033
                if (*p == '\n' || *p == '\r') {
1034
                        *p++ = ' ';
1035
                        spc = 0;
1036
                        while (g_ascii_isspace(*(p + spc)))
1037
                                spc++;
1038
                        if (spc)
1039
                                memmove(p, p + spc, strlen(p + spc) + 1);
1040
                } else
1041
                        p++;
1042
        }
1043
}
1044
1045
void subst_char(gchar *str, gchar orig, gchar subst)
1046
{
1047
        register gchar *p = str;
1048
1049
        while (*p) {
1050
                if (*p == orig)
1051
                        *p = subst;
1052
                p++;
1053
        }
1054
}
1055
1056
void subst_chars(gchar *str, gchar *orig, gchar subst)
1057
{
1058
        register gchar *p = str;
1059
1060
        while (*p) {
1061
                if (strchr(orig, *p) != NULL)
1062
                        *p = subst;
1063
                p++;
1064
        }
1065
}
1066
1067
void subst_null(gchar *str, gint len, gchar subst)
1068
{
1069
        register gchar *p = str;
1070
1071
        while (len--) {
1072
                if (*p == '\0')
1073
                        *p = subst;
1074
                p++;
1075
        }
1076
}
1077
1078
void subst_for_filename(gchar *str)
1079
{
1080
        subst_chars(str, " \t\r\n\"'/\\", '_');
1081
}
1082
1083
gchar *get_alt_filename(const gchar *filename, gint count)
1084
{
1085
        const gchar *ext;
1086
        gchar *alt_filename;
1087
1088
        ext = strrchr(filename, '.');
1089
1090
        if (ext) {
1091
                gchar *base;
1092
1093
                base = g_strndup(filename, ext - filename);
1094
                alt_filename = g_strdup_printf("%s-%d%s", base, count, ext);
1095
                g_free(base);
1096
        } else
1097
                alt_filename = g_strdup_printf("%s-%d", filename, count);
1098
1099
        return alt_filename;
1100
}
1101
1102
gboolean is_header_line(const gchar *str)
1103
{
1104
        if (str[0] == ':') return FALSE;
1105
1106
        while (*str != '\0' && *str != ' ') {
1107
                if (*str == ':')
1108
                        return TRUE;
1109
                str++;
1110
        }
1111
1112
        return FALSE;
1113
}
1114
1115
gboolean is_ascii_str(const gchar *str)
1116
{
1117
        const guchar *p = (const guchar *)str;
1118
1119
        while (*p != '\0') {
1120
                if (*p != '\t' && *p != ' ' &&
1121
                    *p != '\r' && *p != '\n' &&
1122
                    (*p < 32 || *p >= 127))
1123
                        return FALSE;
1124
                p++;
1125
        }
1126
1127
        return TRUE;
1128
}
1129
1130
gint get_quote_level(const gchar *str)
1131
{
1132
        const gchar *first_pos;
1133
        const gchar *last_pos;
1134
        const gchar *p = str;
1135
        gint quote_level = -1;
1136
1137
        /* speed up line processing by only searching to the last '>' */
1138
        if ((first_pos = strchr(str, '>')) != NULL) {
1139
                /* skip a line if it contains a '<' before the initial '>' */
1140
                if (memchr(str, '<', first_pos - str) != NULL)
1141
                        return -1;
1142
                last_pos = strrchr(first_pos, '>');
1143
        } else
1144
                return -1;
1145
1146
        while (p <= last_pos) {
1147
                while (p < last_pos) {
1148
                        if (g_ascii_isspace(*p))
1149
                                p++;
1150
                        else
1151
                                break;
1152
                }
1153
1154
                if (*p == '>')
1155
                        quote_level++;
1156
                else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1157
                        /* any characters are allowed except '-' and space */
1158
                        while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) &&
1159
                               p < last_pos)
1160
                                p++;
1161
                        if (*p == '>')
1162
                                quote_level++;
1163
                        else
1164
                                break;
1165
                }
1166
1167
                p++;
1168
        }
1169
1170
        return quote_level;
1171
}
1172
1173
gint check_line_length(const gchar *str, gint max_chars, gint *line)
1174
{
1175
        const gchar *p = str, *q;
1176
        gint cur_line = 0, len;
1177
1178
        while ((q = strchr(p, '\n')) != NULL) {
1179
                len = q - p + 1;
1180
                if (len > max_chars) {
1181
                        if (line)
1182
                                *line = cur_line;
1183
                        return -1;
1184
                }
1185
                p = q + 1;
1186
                ++cur_line;
1187
        }
1188
1189
        len = strlen(p);
1190
        if (len > max_chars) {
1191
                if (line)
1192
                        *line = cur_line;
1193
                return -1;
1194
        }
1195
1196
        return 0;
1197
}
1198
1199
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1200
{
1201
        register guint haystack_len, needle_len;
1202
        gboolean in_squote = FALSE, in_dquote = FALSE;
1203
1204
        haystack_len = strlen(haystack);
1205
        needle_len   = strlen(needle);
1206
1207
        if (haystack_len < needle_len || needle_len == 0)
1208
                return NULL;
1209
1210
        while (haystack_len >= needle_len) {
1211
                if (!in_squote && !in_dquote &&
1212
                    !strncmp(haystack, needle, needle_len))
1213
                        return (gchar *)haystack;
1214
1215
                /* 'foo"bar"' -> foo"bar"
1216
                   "foo'bar'" -> foo'bar' */
1217
                if (*haystack == '\'') {
1218
                        if (in_squote)
1219
                                in_squote = FALSE;
1220
                        else if (!in_dquote)
1221
                                in_squote = TRUE;
1222
                } else if (*haystack == '\"') {
1223
                        if (in_dquote)
1224
                                in_dquote = FALSE;
1225
                        else if (!in_squote)
1226
                                in_dquote = TRUE;
1227
                }
1228
1229
                haystack++;
1230
                haystack_len--;
1231
        }
1232
1233
        return NULL;
1234
}
1235
1236
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1237
{
1238
        const gchar *p;
1239
        gchar quote_chr = '"';
1240
        gint in_brace;
1241
        gboolean in_quote = FALSE;
1242
1243
        p = str;
1244
1245
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1246
                p++;
1247
                in_brace = 1;
1248
                while (*p) {
1249
                        if (*p == op && !in_quote)
1250
                                in_brace++;
1251
                        else if (*p == cl && !in_quote)
1252
                                in_brace--;
1253
                        else if (*p == quote_chr)
1254
                                in_quote ^= TRUE;
1255
1256
                        if (in_brace == 0)
1257
                                return (gchar *)p;
1258
1259
                        p++;
1260
                }
1261
        }
1262
1263
        return NULL;
1264
}
1265
1266
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1267
                             gint max_tokens)
1268
{
1269
        GSList *string_list = NULL, *slist;
1270
        gchar **str_array;
1271
        const gchar *s_op, *s_cl;
1272
        guint i, n = 1;
1273
1274
        g_return_val_if_fail(str != NULL, NULL);
1275
1276
        if (max_tokens < 1)
1277
                max_tokens = G_MAXINT;
1278
1279
        s_op = strchr_with_skip_quote(str, '"', op);
1280
        if (!s_op) return NULL;
1281
        str = s_op;
1282
        s_cl = strchr_parenthesis_close(str, op, cl);
1283
        if (s_cl) {
1284
                do {
1285
                        guint len;
1286
                        gchar *new_string;
1287
1288
                        str++;
1289
                        len = s_cl - str;
1290
                        new_string = g_new(gchar, len + 1);
1291
                        strncpy(new_string, str, len);
1292
                        new_string[len] = 0;
1293
                        string_list = g_slist_prepend(string_list, new_string);
1294
                        n++;
1295
                        str = s_cl + 1;
1296
1297
                        while (*str && g_ascii_isspace(*str)) str++;
1298
                        if (*str != op) {
1299
                                string_list = g_slist_prepend(string_list,
1300
                                                              g_strdup(""));
1301
                                n++;
1302
                                s_op = strchr_with_skip_quote(str, '"', op);
1303
                                if (!--max_tokens || !s_op) break;
1304
                                str = s_op;
1305
                        } else
1306
                                s_op = str;
1307
                        s_cl = strchr_parenthesis_close(str, op, cl);
1308
                } while (--max_tokens && s_cl);
1309
        }
1310
1311
        str_array = g_new(gchar*, n);
1312
1313
        i = n - 1;
1314
1315
        str_array[i--] = NULL;
1316
        for (slist = string_list; slist; slist = slist->next)
1317
                str_array[i--] = slist->data;
1318
1319
        g_slist_free(string_list);
1320
1321
        return str_array;
1322
}
1323
1324
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1325
                            gint max_tokens)
1326
{
1327
        GSList *string_list = NULL, *slist;
1328
        gchar **str_array, *s, *new_str;
1329
        guint i, n = 1, len;
1330
1331
        g_return_val_if_fail(str != NULL, NULL);
1332
        g_return_val_if_fail(delim != NULL, NULL);
1333
1334
        if (max_tokens < 1)
1335
                max_tokens = G_MAXINT;
1336
1337
        s = strstr_with_skip_quote(str, delim);
1338
        if (s) {
1339
                guint delimiter_len = strlen(delim);
1340
1341
                do {
1342
                        len = s - str;
1343
                        new_str = g_strndup(str, len);
1344
1345
                        if (new_str[0] == '\'' || new_str[0] == '\"') {
1346
                                if (new_str[len - 1] == new_str[0]) {
1347
                                        new_str[len - 1] = '\0';
1348
                                        memmove(new_str, new_str + 1, len - 1);
1349
                                }
1350
                        }
1351
                        string_list = g_slist_prepend(string_list, new_str);
1352
                        n++;
1353
                        str = s + delimiter_len;
1354
                        s = strstr_with_skip_quote(str, delim);
1355
                } while (--max_tokens && s);
1356
        }
1357
1358
        if (*str) {
1359
                new_str = g_strdup(str);
1360
                if (new_str[0] == '\'' || new_str[0] == '\"') {
1361
                        len = strlen(str);
1362
                        if (new_str[len - 1] == new_str[0]) {
1363
                                new_str[len - 1] = '\0';
1364
                                memmove(new_str, new_str + 1, len - 1);
1365
                        }
1366
                }
1367
                string_list = g_slist_prepend(string_list, new_str);
1368
                n++;
1369
        }
1370
1371
        str_array = g_new(gchar*, n);
1372
1373
        i = n - 1;
1374
1375
        str_array[i--] = NULL;
1376
        for (slist = string_list; slist; slist = slist->next)
1377
                str_array[i--] = slist->data;
1378
1379
        g_slist_free(string_list);
1380
1381
        return str_array;
1382
}
1383
1384
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1385
{
1386
        gchar *abbrev_group;
1387
        gchar *ap;
1388
        const gchar *p = group;
1389
        const gchar *last;
1390
1391
        last = group + strlen(group);
1392
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1393
1394
        while (*p) {
1395
                while (*p == '.')
1396
                        *ap++ = *p++;
1397
                if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1398
                        *ap++ = *p++;
1399
                        while (*p != '.') p++;
1400
                } else {
1401
                        strcpy(ap, p);
1402
                        return abbrev_group;
1403
                }
1404
        }
1405
1406
        *ap = '\0';
1407
        return abbrev_group;
1408
}
1409
1410
gchar *trim_string(const gchar *str, gint len)
1411
{
1412
        const gchar *p = str;
1413
        gint mb_len;
1414
        gchar *new_str;
1415
        gint new_len = 0;
1416
1417
        if (!str) return NULL;
1418
        if (strlen(str) <= len)
1419
                return g_strdup(str);
1420
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1421
                return g_strdup(str);
1422
1423
        while (*p != '\0') {
1424
                mb_len = g_utf8_skip[*(guchar *)p];
1425
                if (mb_len == 0)
1426
                        break;
1427
                else if (new_len + mb_len > len)
1428
                        break;
1429
1430
                new_len += mb_len;
1431
                p += mb_len;
1432
        }
1433
1434
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1435
        return g_strconcat(new_str, "...", NULL);
1436
}
1437
1438
gchar *trim_string_before(const gchar *str, gint len)
1439
{
1440
        const gchar *p = str;
1441
        gint mb_len;
1442
        gint new_len;
1443
1444
        if (!str) return NULL;
1445
        if ((new_len = strlen(str)) <= len)
1446
                return g_strdup(str);
1447
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1448
                return g_strdup(str);
1449
1450
        while (*p != '\0') {
1451
                mb_len = g_utf8_skip[*(guchar *)p];
1452
                if (mb_len == 0)
1453
                        break;
1454
1455
                new_len -= mb_len;
1456
                p += mb_len;
1457
1458
                if (new_len <= len)
1459
                        break;
1460
        }
1461
1462
        return g_strconcat("...", p, NULL);
1463
}
1464
1465
GList *uri_list_extract_filenames(const gchar *uri_list)
1466
{
1467
        GList *result = NULL;
1468
        gchar *file;
1469
1470
#if GLIB_CHECK_VERSION(2, 6, 0)
1471
        gchar **uris;
1472
        gint i;
1473
1474
        uris = g_uri_list_extract_uris(uri_list);
1475
        g_return_val_if_fail(uris != NULL, NULL);
1476
1477
        for (i = 0; uris[i] != NULL; i++) {
1478
                file = g_filename_from_uri(uris[i], NULL, NULL);
1479
                if (file)
1480
                        result = g_list_append(result, file);
1481
        }
1482
1483
        g_strfreev(uris);
1484
1485
        return result;
1486
#else
1487
        const gchar *p, *q;
1488
1489
        p = uri_list;
1490
1491
        while (p) {
1492
                if (*p != '#') {
1493
                        while (g_ascii_isspace(*p)) p++;
1494
                        if (!strncmp(p, "file:", 5)) {
1495
                                p += 5;
1496
                                while (*p == '/' && *(p + 1) == '/') p++;
1497
                                q = p;
1498
                                while (*q && *q != '\n' && *q != '\r') q++;
1499
1500
                                if (q > p) {
1501
                                        q--;
1502
                                        while (q > p && g_ascii_isspace(*q))
1503
                                                q--;
1504
                                        file = g_malloc(q - p + 2);
1505
                                        strncpy(file, p, q - p + 1);
1506
                                        file[q - p + 1] = '\0';
1507
                                        decode_uri(file, file);
1508
                                        result = g_list_append(result, file);
1509
                                }
1510
                        }
1511
                }
1512
                p = strchr(p, '\n');
1513
                if (p) p++;
1514
        }
1515
1516
        return result;
1517
#endif
1518
}
1519
1520
#define HEX_TO_INT(val, hex) \
1521
{ \
1522
        gchar c = hex; \
1523
 \
1524
        if ('0' <= c && c <= '9') { \
1525
                val = c - '0'; \
1526
        } else if ('a' <= c && c <= 'f') { \
1527
                val = c - 'a' + 10; \
1528
        } else if ('A' <= c && c <= 'F') { \
1529
                val = c - 'A' + 10; \
1530
        } else { \
1531
                val = 0; \
1532
        } \
1533
}
1534
1535
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1536
 * characters.
1537
 */
1538
static gint axtoi(const gchar *hex_str)
1539
{
1540
        gint hi, lo;
1541
1542
        HEX_TO_INT(hi, hex_str[0]);
1543
        HEX_TO_INT(lo, hex_str[1]);
1544
1545
        return (hi << 4) + lo;
1546
}
1547
1548
gboolean is_uri_string(const gchar *str)
1549
{
1550
        return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1551
                g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1552
                g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1553
                g_ascii_strncasecmp(str, "www.", 4) == 0);
1554
}
1555
1556
gchar *get_uri_path(const gchar *uri)
1557
{
1558
        if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1559
                return (gchar *)(uri + 7);
1560
        else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1561
                return (gchar *)(uri + 8);
1562
        else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1563
                return (gchar *)(uri + 6);
1564
        else
1565
                return (gchar *)uri;
1566
}
1567
1568
gint get_uri_len(const gchar *str)
1569
{
1570
        const gchar *p;
1571
1572
        if (is_uri_string(str)) {
1573
                for (p = str; *p != '\0'; p++) {
1574
                        if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1575
                                break;
1576
                }
1577
                return p - str;
1578
        }
1579
1580
        return 0;
1581
}
1582
1583
/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1584
 * plusses, and escape characters are used)
1585
 * Note: decoded_uri and encoded_uri can point the same location
1586
 */
1587
void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1588
{
1589
        gchar *dec = decoded_uri;
1590
        const gchar *enc = encoded_uri;
1591
1592
        while (*enc) {
1593
                if (*enc == '%') {
1594
                        enc++;
1595
                        if (isxdigit((guchar)enc[0]) &&
1596
                            isxdigit((guchar)enc[1])) {
1597
                                *dec = axtoi(enc);
1598
                                dec++;
1599
                                enc += 2;
1600
                        }
1601
                } else {
1602
                        if (*enc == '+')
1603
                                *dec = ' ';
1604
                        else
1605
                                *dec = *enc;
1606
                        dec++;
1607
                        enc++;
1608
                }
1609
        }
1610
1611
        *dec = '\0';
1612
}
1613
1614
gchar *encode_uri(const gchar *filename)
1615
{
1616
        gchar *uri;
1617
1618
        uri = g_filename_to_uri(filename, NULL, NULL);
1619
        if (!uri)
1620
                uri = g_strconcat("file://", filename, NULL);
1621
1622
        return uri;
1623
}
1624
1625
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
1626
                     gchar **subject, gchar **body)
1627
{
1628
        gchar *tmp_mailto;
1629
        gchar *p;
1630
1631
        Xstrdup_a(tmp_mailto, mailto, return -1);
1632
1633
        if (!strncmp(tmp_mailto, "mailto:", 7))
1634
                tmp_mailto += 7;
1635
1636
        p = strchr(tmp_mailto, '?');
1637
        if (p) {
1638
                *p = '\0';
1639
                p++;
1640
        }
1641
1642
        if (to && !*to)
1643
                *to = g_strdup(tmp_mailto);
1644
1645
        while (p) {
1646
                gchar *field, *value;
1647
1648
                field = p;
1649
1650
                p = strchr(p, '=');
1651
                if (!p) break;
1652
                *p = '\0';
1653
                p++;
1654
1655
                value = p;
1656
1657
                p = strchr(p, '&');
1658
                if (p) {
1659
                        *p = '\0';
1660
                        p++;
1661
                }
1662
1663
                if (*value == '\0') continue;
1664
1665
                if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
1666
                        *cc = g_strdup(value);
1667
                } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
1668
                        *bcc = g_strdup(value);
1669
                } else if (subject && !*subject &&
1670
                           !g_ascii_strcasecmp(field, "subject")) {
1671
                        *subject = g_malloc(strlen(value) + 1);
1672
                        decode_uri(*subject, value);
1673
                } else if (body && !*body &&
1674
                           !g_ascii_strcasecmp(field, "body")) {
1675
                        *body = g_malloc(strlen(value) + 1);
1676
                        decode_uri(*body, value);
1677
                }
1678
        }
1679
1680
        return 0;
1681
}
1682
1683
static gchar *startup_dir = NULL;
1684
static gchar *rc_dir = NULL;
1685
1686
void set_startup_dir(void)
1687
{
1688
        if (!startup_dir)
1689
                startup_dir = g_get_current_dir();
1690
}
1691
1692
void set_rc_dir(const gchar *dir)
1693
{
1694
        if (rc_dir)
1695
                g_free(rc_dir);
1696
1697
        if (dir) {
1698
                if (g_path_is_absolute(dir))
1699
                        rc_dir = g_strdup(dir);
1700
                else
1701
                        rc_dir = g_strconcat(get_startup_dir(),
1702
                                             G_DIR_SEPARATOR_S, dir, NULL);
1703
        } else
1704
                rc_dir = NULL;
1705
}
1706
1707
const gchar *get_startup_dir(void)
1708
{
1709
        if (!startup_dir)
1710
                set_startup_dir();
1711
1712
        return startup_dir;
1713
}
1714
1715
const gchar *get_home_dir(void)
1716
{
1717
#ifdef G_OS_WIN32
1718
        static const gchar *home_dir = NULL;
1719
1720
        if (!home_dir) {
1721
                home_dir = g_get_home_dir();
1722
                if (!home_dir)
1723
                        home_dir = "C:\\Sylpheed";
1724
        }
1725
1726
        return home_dir;
1727
#else
1728
        return g_get_home_dir();
1729
#endif
1730
}
1731
1732
const gchar *get_rc_dir(void)
1733
{
1734
        if (!rc_dir) {
1735
#ifdef G_OS_WIN32
1736
                const gchar *appdata;
1737
1738
                appdata = g_getenv("APPDATA");
1739
                if (appdata)
1740
                        rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S,
1741
                                             RC_DIR, NULL);
1742
                else
1743
                        rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1744
                                             RC_DIR, NULL);
1745
#else
1746
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1747
                                     RC_DIR, NULL);
1748
#endif
1749
        }
1750
1751
        return rc_dir;
1752
}
1753
1754
const gchar *get_old_rc_dir(void)
1755
{
1756
        static gchar *old_rc_dir = NULL;
1757
1758
        if (!old_rc_dir)
1759
                old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1760
                                         OLD_RC_DIR, NULL);
1761
1762
        return old_rc_dir;
1763
}
1764
1765
const gchar *get_mail_base_dir(void)
1766
{
1767
#ifdef G_OS_WIN32
1768
        static gchar *mail_base_dir = NULL;
1769
1770
        if (!mail_base_dir)
1771
                mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1772
                                            "Mailboxes", NULL);
1773
1774
        return mail_base_dir;
1775
#else
1776
        return get_home_dir();
1777
#endif
1778
}
1779
1780
const gchar *get_news_cache_dir(void)
1781
{
1782
        static gchar *news_cache_dir = NULL;
1783
1784
        if (!news_cache_dir)
1785
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1786
                                             NEWS_CACHE_DIR, NULL);
1787
1788
        return news_cache_dir;
1789
}
1790
1791
const gchar *get_imap_cache_dir(void)
1792
{
1793
        static gchar *imap_cache_dir = NULL;
1794
1795
        if (!imap_cache_dir)
1796
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1797
                                             IMAP_CACHE_DIR, NULL);
1798
1799
        return imap_cache_dir;
1800
}
1801
1802
const gchar *get_mime_tmp_dir(void)
1803
{
1804
        static gchar *mime_tmp_dir = NULL;
1805
1806
        if (!mime_tmp_dir)
1807
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1808
                                           MIME_TMP_DIR, NULL);
1809
1810
        return mime_tmp_dir;
1811
}
1812
1813
const gchar *get_template_dir(void)
1814
{
1815
        static gchar *template_dir = NULL;
1816
1817
        if (!template_dir)
1818
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1819
                                           TEMPLATE_DIR, NULL);
1820
1821
        return template_dir;
1822
}
1823
1824
const gchar *get_tmp_dir(void)
1825
{
1826
        static gchar *tmp_dir = NULL;
1827
1828
        if (!tmp_dir)
1829
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1830
                                      TMP_DIR, NULL);
1831
1832
        return tmp_dir;
1833
}
1834
1835
gchar *get_tmp_file(void)
1836
{
1837
        gchar *tmp_file;
1838
        static guint32 id = 0;
1839
1840
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1841
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
1842
1843
        return tmp_file;
1844
}
1845
1846
const gchar *get_domain_name(void)
1847
{
1848
#ifdef G_OS_UNIX
1849
        static gchar *domain_name = NULL;
1850
1851
        if (!domain_name) {
1852
                gchar buf[128] = "";
1853
                struct hostent *hp;
1854
1855
                if (gethostname(buf, sizeof(buf)) < 0) {
1856
                        perror("gethostname");
1857
                        domain_name = "unknown";
1858
                } else {
1859
                        buf[sizeof(buf) - 1] = '\0';
1860
                        if ((hp = my_gethostbyname(buf)) == NULL) {
1861
                                perror("gethostbyname");
1862
                                domain_name = g_strdup(buf);
1863
                        } else {
1864
                                domain_name = g_strdup(hp->h_name);
1865
                        }
1866
                }
1867
1868
                debug_print("domain name = %s\n", domain_name);
1869
        }
1870
1871
        return domain_name;
1872
#else
1873
        return "unknown";
1874
#endif
1875
}
1876
1877
off_t get_file_size(const gchar *file)
1878
{
1879
        struct stat s;
1880
1881
        if (g_stat(file, &s) < 0) {
1882
                FILE_OP_ERROR(file, "stat");
1883
                return -1;
1884
        }
1885
1886
        return s.st_size;
1887
}
1888
1889
off_t get_file_size_as_crlf(const gchar *file)
1890
{
1891
        FILE *fp;
1892
        off_t size = 0;
1893
        gchar buf[BUFFSIZE];
1894
1895
        if ((fp = g_fopen(file, "rb")) == NULL) {
1896
                FILE_OP_ERROR(file, "fopen");
1897
                return -1;
1898
        }
1899
1900
        while (fgets(buf, sizeof(buf), fp) != NULL) {
1901
                strretchomp(buf);
1902
                size += strlen(buf) + 2;
1903
        }
1904
1905
        if (ferror(fp)) {
1906
                FILE_OP_ERROR(file, "fgets");
1907
                size = -1;
1908
        }
1909
1910
        fclose(fp);
1911
1912
        return size;
1913
}
1914
1915
off_t get_left_file_size(FILE *fp)
1916
{
1917
        glong pos;
1918
        glong end;
1919
        off_t size;
1920
1921
        if ((pos = ftell(fp)) < 0) {
1922
                perror("ftell");
1923
                return -1;
1924
        }
1925
        if (fseek(fp, 0L, SEEK_END) < 0) {
1926
                perror("fseek");
1927
                return -1;
1928
        }
1929
        if ((end = ftell(fp)) < 0) {
1930
                perror("fseek");
1931
                return -1;
1932
        }
1933
        size = end - pos;
1934
        if (fseek(fp, pos, SEEK_SET) < 0) {
1935
                perror("fseek");
1936
                return -1;
1937
        }
1938
1939
        return size;
1940
}
1941
1942
gboolean file_exist(const gchar *file, gboolean allow_fifo)
1943
{
1944
        struct stat s;
1945
1946
        if (file == NULL)
1947
                return FALSE;
1948
1949
        if (g_stat(file, &s) < 0) {
1950
                if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1951
                return FALSE;
1952
        }
1953
1954
        if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1955
                return TRUE;
1956
1957
        return FALSE;
1958
}
1959
1960
gboolean is_dir_exist(const gchar *dir)
1961
{
1962
        if (dir == NULL)
1963
                return FALSE;
1964
1965
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
1966
}
1967
1968
gboolean is_file_entry_exist(const gchar *file)
1969
{
1970
        if (file == NULL)
1971
                return FALSE;
1972
1973
        return g_file_test(file, G_FILE_TEST_EXISTS);
1974
}
1975
1976
gboolean dirent_is_regular_file(struct dirent *d)
1977
{
1978
#ifdef HAVE_DIRENT_D_TYPE
1979
        if (d->d_type == DT_REG)
1980
                return TRUE;
1981
        else if (d->d_type != DT_UNKNOWN)
1982
                return FALSE;
1983
#endif
1984
1985
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
1986
}
1987
1988
gboolean dirent_is_directory(struct dirent *d)
1989
{
1990
#ifdef HAVE_DIRENT_D_TYPE
1991
        if (d->d_type == DT_DIR)
1992
                return TRUE;
1993
        else if (d->d_type != DT_UNKNOWN)
1994
                return FALSE;
1995
#endif
1996
1997
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
1998
}
1999
2000
gint change_dir(const gchar *dir)
2001
{
2002
        gchar *prevdir = NULL;
2003
2004
        if (debug_mode)
2005
                prevdir = g_get_current_dir();
2006
2007
        if (g_chdir(dir) < 0) {
2008
                FILE_OP_ERROR(dir, "chdir");
2009
                if (debug_mode) g_free(prevdir);
2010
                return -1;
2011
        } else if (debug_mode) {
2012
                gchar *cwd;
2013
2014
                cwd = g_get_current_dir();
2015
                if (strcmp(prevdir, cwd) != 0)
2016
                        g_print("current dir: %s\n", cwd);
2017
                g_free(cwd);
2018
                g_free(prevdir);
2019
        }
2020
2021
        return 0;
2022
}
2023
2024
gint make_dir(const gchar *dir)
2025
{
2026
        if (g_mkdir(dir, S_IRWXU) < 0) {
2027
                FILE_OP_ERROR(dir, "mkdir");
2028
                return -1;
2029
        }
2030
        if (g_chmod(dir, S_IRWXU) < 0)
2031
                FILE_OP_ERROR(dir, "chmod");
2032
2033
        return 0;
2034
}
2035
2036
gint make_dir_hier(const gchar *dir)
2037
{
2038
        gchar *parent_dir;
2039
        const gchar *p;
2040
2041
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2042
                parent_dir = g_strndup(dir, p - dir);
2043
                if (*parent_dir != '\0') {
2044
                        if (!is_dir_exist(parent_dir)) {
2045
                                if (make_dir(parent_dir) < 0) {
2046
                                        g_free(parent_dir);
2047
                                        return -1;
2048
                                }
2049
                        }
2050
                }
2051
                g_free(parent_dir);
2052
        }
2053
2054
        if (!is_dir_exist(dir)) {
2055
                if (make_dir(dir) < 0)
2056
                        return -1;
2057
        }
2058
2059
        return 0;
2060
}
2061
2062
gint remove_all_files(const gchar *dir)
2063
{
2064
        GDir *dp;
2065
        const gchar *dir_name;
2066
        gchar *prev_dir;
2067
2068
        prev_dir = g_get_current_dir();
2069
2070
        if (g_chdir(dir) < 0) {
2071
                FILE_OP_ERROR(dir, "chdir");
2072
                g_free(prev_dir);
2073
                return -1;
2074
        }
2075
2076
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2077
                g_warning("failed to open directory: %s\n", dir);
2078
                g_free(prev_dir);
2079
                return -1;
2080
        }
2081
2082
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2083
                if (g_unlink(dir_name) < 0)
2084
                        FILE_OP_ERROR(dir_name, "unlink");
2085
        }
2086
2087
        g_dir_close(dp);
2088
2089
        if (g_chdir(prev_dir) < 0) {
2090
                FILE_OP_ERROR(prev_dir, "chdir");
2091
                g_free(prev_dir);
2092
                return -1;
2093
        }
2094
2095
        g_free(prev_dir);
2096
2097
        return 0;
2098
}
2099
2100
gint remove_numbered_files(const gchar *dir, guint first, guint last)
2101
{
2102
        GDir *dp;
2103
        const gchar *dir_name;
2104
        gchar *prev_dir;
2105
        gint file_no;
2106
2107
        prev_dir = g_get_current_dir();
2108
2109
        if (g_chdir(dir) < 0) {
2110
                FILE_OP_ERROR(dir, "chdir");
2111
                g_free(prev_dir);
2112
                return -1;
2113
        }
2114
2115
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2116
                g_warning("failed to open directory: %s\n", dir);
2117
                g_free(prev_dir);
2118
                return -1;
2119
        }
2120
2121
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2122
                file_no = to_number(dir_name);
2123
                if (file_no > 0 && first <= file_no && file_no <= last) {
2124
                        if (is_dir_exist(dir_name))
2125
                                continue;
2126
                        if (g_unlink(dir_name) < 0)
2127
                                FILE_OP_ERROR(dir_name, "unlink");
2128
                }
2129
        }
2130
2131
        g_dir_close(dp);
2132
2133
        if (g_chdir(prev_dir) < 0) {
2134
                FILE_OP_ERROR(prev_dir, "chdir");
2135
                g_free(prev_dir);
2136
                return -1;
2137
        }
2138
2139
        g_free(prev_dir);
2140
2141
        return 0;
2142
}
2143
2144
gint remove_all_numbered_files(const gchar *dir)
2145
{
2146
        return remove_numbered_files(dir, 0, UINT_MAX);
2147
}
2148
2149
gint remove_expired_files(const gchar *dir, guint hours)
2150
{
2151
        GDir *dp;
2152
        const gchar *dir_name;
2153
        struct stat s;
2154
        gchar *prev_dir;
2155
        gint file_no;
2156
        time_t mtime, now, expire_time;
2157
2158
        prev_dir = g_get_current_dir();
2159
2160
        if (g_chdir(dir) < 0) {
2161
                FILE_OP_ERROR(dir, "chdir");
2162
                g_free(prev_dir);
2163
                return -1;
2164
        }
2165
2166
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2167
                g_warning("failed to open directory: %s\n", dir);
2168
                g_free(prev_dir);
2169
                return -1;
2170
        }
2171
2172
        now = time(NULL);
2173
        expire_time = hours * 60 * 60;
2174
2175
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2176
                file_no = to_number(dir_name);
2177
                if (file_no > 0) {
2178
                        if (g_stat(dir_name, &s) < 0) {
2179
                                FILE_OP_ERROR(dir_name, "stat");
2180
                                continue;
2181
                        }
2182
                        if (S_ISDIR(s.st_mode))
2183
                                continue;
2184
                        mtime = MAX(s.st_mtime, s.st_atime);
2185
                        if (now - mtime > expire_time) {
2186
                                if (g_unlink(dir_name) < 0)
2187
                                        FILE_OP_ERROR(dir_name, "unlink");
2188
                        }
2189
                }
2190
        }
2191
2192
        g_dir_close(dp);
2193
2194
        if (g_chdir(prev_dir) < 0) {
2195
                FILE_OP_ERROR(prev_dir, "chdir");
2196
                g_free(prev_dir);
2197
                return -1;
2198
        }
2199
2200
        g_free(prev_dir);
2201
2202
        return 0;
2203
}
2204
2205
static gint remove_dir_recursive_real(const gchar *dir)
2206
{
2207
        struct stat s;
2208
        GDir *dp;
2209
        const gchar *dir_name;
2210
        gchar *prev_dir;
2211
2212
        if (g_stat(dir, &s) < 0) {
2213
                FILE_OP_ERROR(dir, "stat");
2214
                if (ENOENT == errno) return 0;
2215
                return -1;
2216
        }
2217
2218
        if (!S_ISDIR(s.st_mode)) {
2219
                if (g_unlink(dir) < 0) {
2220
                        FILE_OP_ERROR(dir, "unlink");
2221
                        return -1;
2222
                }
2223
2224
                return 0;
2225
        }
2226
2227
        prev_dir = g_get_current_dir();
2228
        /* g_print("prev_dir = %s\n", prev_dir); */
2229
2230
        if (g_chdir(dir) < 0) {
2231
                FILE_OP_ERROR(dir, "chdir");
2232
                g_free(prev_dir);
2233
                return -1;
2234
        }
2235
2236
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2237
                g_warning("failed to open directory: %s\n", dir);
2238
                g_chdir(prev_dir);
2239
                g_free(prev_dir);
2240
                return -1;
2241
        }
2242
2243
        /* remove all files in the directory */
2244
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2245
                /* g_print("removing %s\n", dir_name); */
2246
2247
                if (is_dir_exist(dir_name)) {
2248
                        if (remove_dir_recursive_real(dir_name) < 0) {
2249
                                g_warning("can't remove directory\n");
2250
                                return -1;
2251
                        }
2252
                } else {
2253
                        if (g_unlink(dir_name) < 0)
2254
                                FILE_OP_ERROR(dir_name, "unlink");
2255
                }
2256
        }
2257
2258
        g_dir_close(dp);
2259
2260
        if (g_chdir(prev_dir) < 0) {
2261
                FILE_OP_ERROR(prev_dir, "chdir");
2262
                g_free(prev_dir);
2263
                return -1;
2264
        }
2265
2266
        g_free(prev_dir);
2267
2268
        if (g_rmdir(dir) < 0) {
2269
                FILE_OP_ERROR(dir, "rmdir");
2270
                return -1;
2271
        }
2272
2273
        return 0;
2274
}
2275
2276
gint remove_dir_recursive(const gchar *dir)
2277
{
2278
        gchar *cur_dir;
2279
        gint ret;
2280
2281
        cur_dir = g_get_current_dir();
2282
2283
        if (g_chdir(dir) < 0) {
2284
                FILE_OP_ERROR(dir, "chdir");
2285
                ret = -1;
2286
                goto leave;
2287
        }
2288
        if (g_chdir("..") < 0) {
2289
                FILE_OP_ERROR(dir, "chdir");
2290
                ret = -1;
2291
                goto leave;
2292
        }
2293
2294
        ret = remove_dir_recursive_real(dir);
2295
2296
leave:
2297
        if (is_dir_exist(cur_dir)) {
2298
                if (g_chdir(cur_dir) < 0) {
2299
                        FILE_OP_ERROR(cur_dir, "chdir");
2300
                }
2301
        }
2302
2303
        g_free(cur_dir);
2304
2305
        return ret;
2306
}
2307
2308
gint rename_force(const gchar *oldpath, const gchar *newpath)
2309
{
2310
#ifndef G_OS_UNIX
2311
        if (!is_file_entry_exist(oldpath)) {
2312
                errno = ENOENT;
2313
                return -1;
2314
        }
2315
        if (is_file_exist(newpath)) {
2316
                if (g_unlink(newpath) < 0)
2317
                        FILE_OP_ERROR(newpath, "unlink");
2318
        }
2319
#endif
2320
        return g_rename(oldpath, newpath);
2321
}
2322
2323
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2324
{
2325
        FILE *src_fp, *dest_fp;
2326
        gint n_read;
2327
        gchar buf[BUFSIZ];
2328
        gchar *dest_bak = NULL;
2329
        gboolean err = FALSE;
2330
2331
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
2332
                FILE_OP_ERROR(src, "fopen");
2333
                return -1;
2334
        }
2335
        if (is_file_exist(dest)) {
2336
                dest_bak = g_strconcat(dest, ".bak", NULL);
2337
                if (rename_force(dest, dest_bak) < 0) {
2338
                        FILE_OP_ERROR(dest, "rename");
2339
                        fclose(src_fp);
2340
                        g_free(dest_bak);
2341
                        return -1;
2342
                }
2343
        }
2344
2345
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2346
                FILE_OP_ERROR(dest, "fopen");
2347
                fclose(src_fp);
2348
                if (dest_bak) {
2349
                        if (rename_force(dest_bak, dest) < 0)
2350
                                FILE_OP_ERROR(dest_bak, "rename");
2351
                        g_free(dest_bak);
2352
                }
2353
                return -1;
2354
        }
2355
2356
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2357
                FILE_OP_ERROR(dest, "chmod");
2358
                g_warning(_("can't change file mode\n"));
2359
        }
2360
2361
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2362
                if (n_read < sizeof(buf) && ferror(src_fp))
2363
                        break;
2364
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2365
                        g_warning(_("writing to %s failed.\n"), dest);
2366
                        fclose(dest_fp);
2367
                        fclose(src_fp);
2368
                        g_unlink(dest);
2369
                        if (dest_bak) {
2370
                                if (rename_force(dest_bak, dest) < 0)
2371
                                        FILE_OP_ERROR(dest_bak, "rename");
2372
                                g_free(dest_bak);
2373
                        }
2374
                        return -1;
2375
                }
2376
        }
2377
2378
        if (ferror(src_fp)) {
2379
                FILE_OP_ERROR(src, "fread");
2380
                err = TRUE;
2381
        }
2382
        fclose(src_fp);
2383
        if (fclose(dest_fp) == EOF) {
2384
                FILE_OP_ERROR(dest, "fclose");
2385
                err = TRUE;
2386
        }
2387
2388
        if (err) {
2389
                g_unlink(dest);
2390
                if (dest_bak) {
2391
                        if (rename_force(dest_bak, dest) < 0)
2392
                                FILE_OP_ERROR(dest_bak, "rename");
2393
                        g_free(dest_bak);
2394
                }
2395
                return -1;
2396
        }
2397
2398
        if (keep_backup == FALSE && dest_bak)
2399
                g_unlink(dest_bak);
2400
2401
        g_free(dest_bak);
2402
2403
        return 0;
2404
}
2405
2406
gint copy_dir(const gchar *src, const gchar *dest)
2407
{
2408
        GDir *dir;
2409
        const gchar *dir_name;
2410
        gchar *src_file;
2411
        gchar *dest_file;
2412
2413
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
2414
                g_warning("failed to open directory: %s\n", src);
2415
                return -1;
2416
        }
2417
2418
        if (make_dir_hier(dest) < 0) {
2419
                g_dir_close(dir);
2420
                return -1;
2421
        }
2422
2423
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
2424
                src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
2425
                dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
2426
                                        NULL);
2427
                if (is_file_exist(src_file))
2428
                        copy_file(src_file, dest_file, FALSE);
2429
                g_free(dest_file);
2430
                g_free(src_file);
2431
        }
2432
2433
        g_dir_close(dir);
2434
2435
        return 0;
2436
}
2437
2438
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2439
{
2440
        if (overwrite == FALSE && is_file_exist(dest)) {
2441
                g_warning("move_file(): file %s already exists.", dest);
2442
                return -1;
2443
        }
2444
2445
        if (rename_force(src, dest) == 0) return 0;
2446
2447
        if (EXDEV != errno) {
2448
                FILE_OP_ERROR(src, "rename");
2449
                return -1;
2450
        }
2451
2452
        if (copy_file(src, dest, FALSE) < 0) return -1;
2453
2454
        g_unlink(src);
2455
2456
        return 0;
2457
}
2458
2459
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2460
{
2461
        FILE *dest_fp;
2462
        gint n_read;
2463
        gint bytes_left, to_read;
2464
        gchar buf[BUFSIZ];
2465
        gboolean err = FALSE;
2466
2467
        if (fseek(fp, offset, SEEK_SET) < 0) {
2468
                perror("fseek");
2469
                return -1;
2470
        }
2471
2472
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2473
                FILE_OP_ERROR(dest, "fopen");
2474
                return -1;
2475
        }
2476
2477
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2478
                FILE_OP_ERROR(dest, "chmod");
2479
                g_warning("can't change file mode\n");
2480
        }
2481
2482
        bytes_left = length;
2483
        to_read = MIN(bytes_left, sizeof(buf));
2484
2485
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2486
                if (n_read < to_read && ferror(fp))
2487
                        break;
2488
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
2489
                        g_warning(_("writing to %s failed.\n"), dest);
2490
                        fclose(dest_fp);
2491
                        g_unlink(dest);
2492
                        return -1;
2493
                }
2494
                bytes_left -= n_read;
2495
                if (bytes_left == 0)
2496
                        break;
2497
                to_read = MIN(bytes_left, sizeof(buf));
2498
        }
2499
2500
        if (ferror(fp)) {
2501
                perror("fread");
2502
                err = TRUE;
2503
        }
2504
        if (fclose(dest_fp) == EOF) {
2505
                FILE_OP_ERROR(dest, "fclose");
2506
                err = TRUE;
2507
        }
2508
2509
        if (err) {
2510
                g_unlink(dest);
2511
                return -1;
2512
        }
2513
2514
        return 0;
2515
}
2516
2517
/* convert line endings into CRLF. If the last line doesn't end with
2518
 * linebreak, add it.
2519
 */
2520
gchar *canonicalize_str(const gchar *str)
2521
{
2522
        const gchar *p;
2523
        guint new_len = 0;
2524
        gchar *out, *outp;
2525
2526
        for (p = str; *p != '\0'; ++p) {
2527
                if (*p != '\r') {
2528
                        ++new_len;
2529
                        if (*p == '\n')
2530
                                ++new_len;
2531
                }
2532
        }
2533
        if (p == str || *(p - 1) != '\n')
2534
                new_len += 2;
2535
2536
        out = outp = g_malloc(new_len + 1);
2537
        for (p = str; *p != '\0'; ++p) {
2538
                if (*p != '\r') {
2539
                        if (*p == '\n')
2540
                                *outp++ = '\r';
2541
                        *outp++ = *p;
2542
                }
2543
        }
2544
        if (p == str || *(p - 1) != '\n') {
2545
                *outp++ = '\r';
2546
                *outp++ = '\n';
2547
        }
2548
        *outp = '\0';
2549
2550
        return out;
2551
}
2552
2553
gint canonicalize_file(const gchar *src, const gchar *dest)
2554
{
2555
        FILE *src_fp, *dest_fp;
2556
        gchar buf[BUFFSIZE];
2557
        gint len;
2558
        gboolean err = FALSE;
2559
        gboolean last_linebreak = FALSE;
2560
2561
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
2562
                FILE_OP_ERROR(src, "fopen");
2563
                return -1;
2564
        }
2565
2566
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2567
                FILE_OP_ERROR(dest, "fopen");
2568
                fclose(src_fp);
2569
                return -1;
2570
        }
2571
2572
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2573
                FILE_OP_ERROR(dest, "chmod");
2574
                g_warning("can't change file mode\n");
2575
        }
2576
2577
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2578
                gint r = 0;
2579
2580
                len = strlen(buf);
2581
                if (len == 0) break;
2582
                last_linebreak = FALSE;
2583
2584
                if (buf[len - 1] != '\n') {
2585
                        last_linebreak = TRUE;
2586
                        r = fputs(buf, dest_fp);
2587
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2588
                        r = fputs(buf, dest_fp);
2589
                } else {
2590
                        if (len > 1) {
2591
                                r = fwrite(buf, len - 1, 1, dest_fp);
2592
                                if (r != 1)
2593
                                        r = EOF;
2594
                        }
2595
                        if (r != EOF)
2596
                                r = fputs("\r\n", dest_fp);
2597
                }
2598
2599
                if (r == EOF) {
2600
                        g_warning("writing to %s failed.\n", dest);
2601
                        fclose(dest_fp);
2602
                        fclose(src_fp);
2603
                        g_unlink(dest);
2604
                        return -1;
2605
                }
2606
        }
2607
2608
        if (last_linebreak == TRUE) {
2609
                if (fputs("\r\n", dest_fp) == EOF)
2610
                        err = TRUE;
2611
        }
2612
2613
        if (ferror(src_fp)) {
2614
                FILE_OP_ERROR(src, "fgets");
2615
                err = TRUE;
2616
        }
2617
        fclose(src_fp);
2618
        if (fclose(dest_fp) == EOF) {
2619
                FILE_OP_ERROR(dest, "fclose");
2620
                err = TRUE;
2621
        }
2622
2623
        if (err) {
2624
                g_unlink(dest);
2625
                return -1;
2626
        }
2627
2628
        return 0;
2629
}
2630
2631
gint canonicalize_file_replace(const gchar *file)
2632
{
2633
        gchar *tmp_file;
2634
2635
        tmp_file = get_tmp_file();
2636
2637
        if (canonicalize_file(file, tmp_file) < 0) {
2638
                g_free(tmp_file);
2639
                return -1;
2640
        }
2641
2642
        if (move_file(tmp_file, file, TRUE) < 0) {
2643
                g_warning("can't replace %s .\n", file);
2644
                g_unlink(tmp_file);
2645
                g_free(tmp_file);
2646
                return -1;
2647
        }
2648
2649
        g_free(tmp_file);
2650
        return 0;
2651
}
2652
2653
gint uncanonicalize_file(const gchar *src, const gchar *dest)
2654
{
2655
        FILE *src_fp, *dest_fp;
2656
        gchar buf[BUFFSIZE];
2657
        gboolean err = FALSE;
2658
2659
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
2660
                FILE_OP_ERROR(src, "fopen");
2661
                return -1;
2662
        }
2663
2664
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2665
                FILE_OP_ERROR(dest, "fopen");
2666
                fclose(src_fp);
2667
                return -1;
2668
        }
2669
2670
        if (change_file_mode_rw(dest_fp, dest) < 0) {
2671
                FILE_OP_ERROR(dest, "chmod");
2672
                g_warning("can't change file mode\n");
2673
        }
2674
2675
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2676
                strcrchomp(buf);
2677
                if (fputs(buf, dest_fp) == EOF) {
2678
                        g_warning("writing to %s failed.\n", dest);
2679
                        fclose(dest_fp);
2680
                        fclose(src_fp);
2681
                        g_unlink(dest);
2682
                        return -1;
2683
                }
2684
        }
2685
2686
        if (ferror(src_fp)) {
2687
                FILE_OP_ERROR(src, "fgets");
2688
                err = TRUE;
2689
        }
2690
        fclose(src_fp);
2691
        if (fclose(dest_fp) == EOF) {
2692
                FILE_OP_ERROR(dest, "fclose");
2693
                err = TRUE;
2694
        }
2695
2696
        if (err) {
2697
                g_unlink(dest);
2698
                return -1;
2699
        }
2700
2701
        return 0;
2702
}
2703
2704
gint uncanonicalize_file_replace(const gchar *file)
2705
{
2706
        gchar *tmp_file;
2707
2708
        tmp_file = get_tmp_file();
2709
2710
        if (uncanonicalize_file(file, tmp_file) < 0) {
2711
                g_free(tmp_file);
2712
                return -1;
2713
        }
2714
2715
        if (move_file(tmp_file, file, TRUE) < 0) {
2716
                g_warning("can't replace %s .\n", file);
2717
                g_unlink(tmp_file);
2718
                g_free(tmp_file);
2719
                return -1;
2720
        }
2721
2722
        g_free(tmp_file);
2723
        return 0;
2724
}
2725
2726
gchar *normalize_newlines(const gchar *str)
2727
{
2728
        const gchar *p = str;
2729
        gchar *out, *outp;
2730
2731
        out = outp = g_malloc(strlen(str) + 1);
2732
        for (p = str; *p != '\0'; ++p) {
2733
                if (*p == '\r') {
2734
                        if (*(p + 1) != '\n')
2735
                                *outp++ = '\n';
2736
                } else
2737
                        *outp++ = *p;
2738
        }
2739
2740
        *outp = '\0';
2741
2742
        return out;
2743
}
2744
2745
gchar *get_outgoing_rfc2822_str(FILE *fp)
2746
{
2747
        gchar buf[BUFFSIZE];
2748
        GString *str;
2749
        gchar *ret;
2750
2751
        str = g_string_new(NULL);
2752
2753
        /* output header part */
2754
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2755
                strretchomp(buf);
2756
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2757
                        gint next;
2758
2759
                        for (;;) {
2760
                                next = fgetc(fp);
2761
                                if (next == EOF)
2762
                                        break;
2763
                                else if (next != ' ' && next != '\t') {
2764
                                        ungetc(next, fp);
2765
                                        break;
2766
                                }
2767
                                if (fgets(buf, sizeof(buf), fp) == NULL)
2768
                                        break;
2769
                        }
2770
#if 0
2771
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
2772
                        get_rfc822_date(buf, sizeof(buf));
2773
                        g_string_append_printf(str, "Date: %s\r\n", buf);
2774
#endif
2775
                } else {
2776
                        g_string_append(str, buf);
2777
                        g_string_append(str, "\r\n");
2778
                        if (buf[0] == '\0')
2779
                                break;
2780
                }
2781
        }
2782
2783
        /* output body part */
2784
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2785
                strretchomp(buf);
2786
                if (buf[0] == '.')
2787
                        g_string_append_c(str, '.');
2788
                g_string_append(str, buf);
2789
                g_string_append(str, "\r\n");
2790
        }
2791
2792
        ret = str->str;
2793
        g_string_free(str, FALSE);
2794
2795
        return ret;
2796
}
2797
2798
/*
2799
 * Create a new boundary in a way that it is very unlikely that this
2800
 * will occur in the following text.  It would be easy to ensure
2801
 * uniqueness if everything is either quoted-printable or base64
2802
 * encoded (note that conversion is allowed), but because MIME bodies
2803
 * may be nested, it may happen that the same boundary has already
2804
 * been used. We avoid scanning the message for conflicts and hope the
2805
 * best.
2806
 *
2807
 *   boundary := 0*69<bchars> bcharsnospace
2808
 *   bchars := bcharsnospace / " "
2809
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2810
 *                    "+" / "_" / "," / "-" / "." /
2811
 *                    "/" / ":" / "=" / "?"
2812
 *
2813
 * some special characters removed because of buggy MTAs
2814
 */
2815
2816
gchar *generate_mime_boundary(const gchar *prefix)
2817
{
2818
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2819
                             "abcdefghijklmnopqrstuvwxyz"
2820
                             "1234567890+_./=";
2821
        gchar buf_uniq[17];
2822
        gchar buf_date[64];
2823
        gint i;
2824
2825
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2826
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2827
        buf_uniq[i] = '\0';
2828
2829
        get_rfc822_date(buf_date, sizeof(buf_date));
2830
        subst_char(buf_date, ' ', '_');
2831
        subst_char(buf_date, ',', '_');
2832
        subst_char(buf_date, ':', '_');
2833
2834
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
2835
                               buf_date, buf_uniq);
2836
}
2837
2838
gint change_file_mode_rw(FILE *fp, const gchar *file)
2839
{
2840
#if HAVE_FCHMOD
2841
        return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2842
#else
2843
        return g_chmod(file, S_IRUSR|S_IWUSR);
2844
#endif
2845
}
2846
2847
#ifdef G_OS_WIN32
2848
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
2849
{
2850
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2851
                wchar_t *wpath;
2852
                wchar_t *wprefix;
2853
                wchar_t *wname;
2854
                gint save_errno;
2855
                gchar *name;
2856
2857
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
2858
                if (wpath == NULL) {
2859
                        errno = EINVAL;
2860
                        return NULL;
2861
                }
2862
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
2863
                if (wprefix == NULL) {
2864
                        errno = EINVAL;
2865
                        g_free(wpath);
2866
                        return NULL;
2867
                }
2868
2869
                wname = _wtempnam(wpath, wprefix);
2870
                save_errno = errno;
2871
2872
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
2873
                if (name == NULL) {
2874
                        save_errno = EINVAL;
2875
                }
2876
2877
                g_free(wname);
2878
                g_free(wprefix);
2879
                g_free(wpath);
2880
2881
                errno = save_errno;
2882
                return name;
2883
        } else {
2884
                gchar *cp_path;
2885
                gchar *cp_prefix;
2886
                gchar *cp_name;
2887
                gint save_errno;
2888
                gchar *name;
2889
2890
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
2891
                if (cp_path == NULL) {
2892
                        errno = EINVAL;
2893
                        return NULL;
2894
                }
2895
2896
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
2897
                if (cp_prefix == NULL) {
2898
                        errno = EINVAL;
2899
                        g_free(cp_path);
2900
                        return NULL;
2901
                }
2902
2903
                cp_name = _tempnam(cp_path, cp_prefix);
2904
                save_errno = errno;
2905
2906
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
2907
                if (name == NULL) {
2908
                        save_errno = EINVAL;
2909
                }
2910
2911
                g_free(cp_name);
2912
                g_free(cp_prefix);
2913
                g_free(cp_path);
2914
2915
                errno = save_errno;
2916
                return name;
2917
        }
2918
}
2919
#endif
2920
2921
FILE *my_tmpfile(void)
2922
{
2923
#ifdef G_OS_WIN32
2924
        const gchar *tmpdir;
2925
        gchar *fname;
2926
        gint fd;
2927
        FILE *fp;
2928
2929
        tmpdir = get_tmp_dir();
2930
        fname = _s_tempnam(tmpdir, "sylph");
2931
        if (!fname)
2932
                return NULL;
2933
2934
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
2935
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
2936
        if (fd < 0) {
2937
                g_free(fname);
2938
                return NULL;
2939
        }
2940
2941
        fp = fdopen(fd, "w+b");
2942
        if (!fp) {
2943
                perror("fdopen");
2944
                close(fd);
2945
        }
2946
2947
        return fp;
2948
#else
2949
        const gchar suffix[] = ".XXXXXX";
2950
        const gchar *tmpdir;
2951
        guint tmplen;
2952
        const gchar *progname;
2953
        guint proglen;
2954
        gchar *fname;
2955
        gint fd;
2956
        FILE *fp;
2957
2958
        tmpdir = get_tmp_dir();
2959
        tmplen = strlen(tmpdir);
2960
        progname = g_get_prgname();
2961
        proglen = strlen(progname);
2962
        Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2963
                return tmpfile());
2964
2965
        memcpy(fname, tmpdir, tmplen);
2966
        fname[tmplen] = G_DIR_SEPARATOR;
2967
        memcpy(fname + tmplen + 1, progname, proglen);
2968
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2969
2970
        fd = g_mkstemp(fname);
2971
        if (fd < 0)
2972
                return tmpfile();
2973
2974
        g_unlink(fname);
2975
2976
        fp = fdopen(fd, "w+b");
2977
        if (!fp)
2978
                close(fd);
2979
2980
        return fp;
2981
#endif
2982
}
2983
2984
FILE *str_open_as_stream(const gchar *str)
2985
{
2986
        FILE *fp;
2987
        size_t len;
2988
2989
        g_return_val_if_fail(str != NULL, NULL);
2990
2991
        fp = my_tmpfile();
2992
        if (!fp) {
2993
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2994
                return NULL;
2995
        }
2996
2997
        len = strlen(str);
2998
        if (len == 0) return fp;
2999
3000
        if (fwrite(str, len, 1, fp) != 1) {
3001
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3002
                fclose(fp);
3003
                return NULL;
3004
        }
3005
3006
        rewind(fp);
3007
        return fp;
3008
}
3009
3010
gint str_write_to_file(const gchar *str, const gchar *file)
3011
{
3012
        FILE *fp;
3013
        size_t len;
3014
3015
        g_return_val_if_fail(str != NULL, -1);
3016
        g_return_val_if_fail(file != NULL, -1);
3017
3018
        if ((fp = g_fopen(file, "wb")) == NULL) {
3019
                FILE_OP_ERROR(file, "fopen");
3020
                return -1;
3021
        }
3022
3023
        len = strlen(str);
3024
        if (len == 0) {
3025
                fclose(fp);
3026
                return 0;
3027
        }
3028
3029
        if (fwrite(str, len, 1, fp) != 1) {
3030
                FILE_OP_ERROR(file, "fwrite");
3031
                fclose(fp);
3032
                g_unlink(file);
3033
                return -1;
3034
        }
3035
3036
        if (fclose(fp) == EOF) {
3037
                FILE_OP_ERROR(file, "fclose");
3038
                g_unlink(file);
3039
                return -1;
3040
        }
3041
3042
        return 0;
3043
}
3044
3045
gchar *file_read_to_str(const gchar *file)
3046
{
3047
        FILE *fp;
3048
        gchar *str;
3049
3050
        g_return_val_if_fail(file != NULL, NULL);
3051
3052
        if ((fp = g_fopen(file, "rb")) == NULL) {
3053
                FILE_OP_ERROR(file, "fopen");
3054
                return NULL;
3055
        }
3056
3057
        str = file_read_stream_to_str(fp);
3058
3059
        fclose(fp);
3060
3061
        return str;
3062
}
3063
3064
gchar *file_read_stream_to_str(FILE *fp)
3065
{
3066
        GByteArray *array;
3067
        guchar buf[BUFSIZ];
3068
        gint n_read;
3069
        gchar *str;
3070
3071
        g_return_val_if_fail(fp != NULL, NULL);
3072
3073
        array = g_byte_array_new();
3074
3075
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3076
                if (n_read < sizeof(buf) && ferror(fp))
3077
                        break;
3078
                g_byte_array_append(array, buf, n_read);
3079
        }
3080
3081
        if (ferror(fp)) {
3082
                FILE_OP_ERROR("file stream", "fread");
3083
                g_byte_array_free(array, TRUE);
3084
                return NULL;
3085
        }
3086
3087
        buf[0] = '\0';
3088
        g_byte_array_append(array, buf, 1);
3089
        str = (gchar *)array->data;
3090
        g_byte_array_free(array, FALSE);
3091
3092
        return str;
3093
}
3094
3095
gint execute_async(gchar *const argv[])
3096
{
3097
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3098
3099
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3100
                          NULL, NULL, NULL, NULL) == FALSE) {
3101
                g_warning("Can't execute command: %s\n", argv[0]);
3102
                return -1;
3103
        }
3104
3105
        return 0;
3106
}
3107
3108
gint execute_sync(gchar *const argv[])
3109
{
3110
        gint status;
3111
3112
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3113
3114
#ifdef G_OS_WIN32
3115
        if (g_spawn_sync(NULL, (gchar **)argv, NULL,
3116
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
3117
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3118
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3119
                g_warning("Can't execute command: %s\n", argv[0]);
3120
                return -1;
3121
        }
3122
3123
        return status;
3124
#else
3125
        if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3126
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3127
                g_warning("Can't execute command: %s\n", argv[0]);
3128
                return -1;
3129
        }
3130
3131
        if (WIFEXITED(status))
3132
                return WEXITSTATUS(status);
3133
        else
3134
                return -1;
3135
#endif
3136
}
3137
3138
gint execute_command_line(const gchar *cmdline, gboolean async)
3139
{
3140
        gchar **argv;
3141
        gint ret;
3142
3143
        debug_print("execute_command_line(): executing: %s\n", cmdline);
3144
3145
        argv = strsplit_with_quote(cmdline, " ", 0);
3146
3147
        if (async)
3148
                ret = execute_async(argv);
3149
        else
3150
                ret = execute_sync(argv);
3151
3152
        g_strfreev(argv);
3153
3154
        return ret;
3155
}
3156
3157
gint execute_open_file(const gchar *file, const gchar *content_type)
3158
{
3159
        g_return_val_if_fail(file != NULL, -1);
3160
3161
#ifdef G_OS_WIN32
3162
        log_print("opening %s - %s\n", file, content_type ? content_type : "");
3163
3164
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3165
                wchar_t *wpath;
3166
3167
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3168
                if (wpath == NULL)
3169
                        return -1;
3170
3171
                ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL);
3172
3173
                g_free(wpath);
3174
3175
                return 0;
3176
        } else {
3177
                gchar *cp_path;
3178
3179
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3180
                if (cp_path == NULL)
3181
                        return -1;
3182
3183
                ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL);
3184
3185
                g_free(cp_path);
3186
3187
                return 0;
3188
        }
3189
#endif
3190
        return 0;
3191
}
3192
3193
gint execute_print_file(const gchar *file)
3194
{
3195
        g_return_val_if_fail(file != NULL, -1);
3196
3197
#ifdef G_OS_WIN32
3198
        log_print("printing %s\n", file);
3199
3200
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3201
                wchar_t *wpath;
3202
3203
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3204
                if (wpath == NULL)
3205
                        return -1;
3206
3207
                ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL);
3208
3209
                g_free(wpath);
3210
3211
                return 0;
3212
        } else {
3213
                gchar *cp_path;
3214
3215
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3216
                if (cp_path == NULL)
3217
                        return -1;
3218
3219
                ShellExecuteA(NULL, "print", cp_path, NULL, NULL,
3220
                              SW_SHOWNORMAL);
3221
3222
                g_free(cp_path);
3223
3224
                return 0;
3225
        }
3226
#endif
3227
        return 0;
3228
}
3229
3230
gchar *get_command_output(const gchar *cmdline)
3231
{
3232
        gchar *child_stdout;
3233
        gint status;
3234
3235
        g_return_val_if_fail(cmdline != NULL, NULL);
3236
3237
        debug_print("get_command_output(): executing: %s\n", cmdline);
3238
3239
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3240
                                      NULL) == FALSE) {
3241
                g_warning("Can't execute command: %s\n", cmdline);
3242
                return NULL;
3243
        }
3244
3245
        return child_stdout;
3246
}
3247
3248
gint open_uri(const gchar *uri, const gchar *cmdline)
3249
{
3250
        gchar buf[BUFFSIZE];
3251
        gchar *p;
3252
3253
        g_return_val_if_fail(uri != NULL, -1);
3254
3255
#ifdef G_OS_WIN32
3256
        if (!cmdline || cmdline[0] == '\0')
3257
                return execute_open_file(uri, NULL);
3258
#endif
3259
3260
        if (cmdline &&
3261
            (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3262
            !strchr(p + 2, '%'))
3263
                g_snprintf(buf, sizeof(buf), cmdline, uri);
3264
        else {
3265
                if (cmdline)
3266
                        g_warning("Open URI command line is invalid "
3267
                                  "(there must be only one '%%s'): %s",
3268
                                  cmdline);
3269
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
3270
        }
3271
3272
        execute_command_line(buf, TRUE);
3273
3274
        return 0;
3275
}
3276
3277
time_t remote_tzoffset_sec(const gchar *zone)
3278
{
3279
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3280
        gchar zone3[4];
3281
        gchar *p;
3282
        gchar c;
3283
        gint iustz;
3284
        gint offset;
3285
        time_t remoteoffset;
3286
3287
        strncpy(zone3, zone, 3);
3288
        zone3[3] = '\0';
3289
        remoteoffset = 0;
3290
3291
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3292
            (c == '+' || c == '-')) {
3293
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3294
                if (c == '-')
3295
                        remoteoffset = -remoteoffset;
3296
        } else if (!strncmp(zone, "UT" , 2) ||
3297
                   !strncmp(zone, "GMT", 2)) {
3298
                remoteoffset = 0;
3299
        } else if (strlen(zone3) == 3) {
3300
                for (p = ustzstr; *p != '\0'; p += 3) {
3301
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
3302
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3303
                                remoteoffset = iustz * 3600;
3304
                                break;
3305
                        }
3306
                }
3307
                if (*p == '\0')
3308
                        return -1;
3309
        } else if (strlen(zone3) == 1) {
3310
                switch (zone[0]) {
3311
                case 'Z': remoteoffset =   0; break;
3312
                case 'A': remoteoffset =  -1; break;
3313
                case 'B': remoteoffset =  -2; break;
3314
                case 'C': remoteoffset =  -3; break;
3315
                case 'D': remoteoffset =  -4; break;
3316
                case 'E': remoteoffset =  -5; break;
3317
                case 'F': remoteoffset =  -6; break;
3318
                case 'G': remoteoffset =  -7; break;
3319
                case 'H': remoteoffset =  -8; break;
3320
                case 'I': remoteoffset =  -9; break;
3321
                case 'K': remoteoffset = -10; break; /* J is not used */
3322
                case 'L': remoteoffset = -11; break;
3323
                case 'M': remoteoffset = -12; break;
3324
                case 'N': remoteoffset =   1; break;
3325
                case 'O': remoteoffset =   2; break;
3326
                case 'P': remoteoffset =   3; break;
3327
                case 'Q': remoteoffset =   4; break;
3328
                case 'R': remoteoffset =   5; break;
3329
                case 'S': remoteoffset =   6; break;
3330
                case 'T': remoteoffset =   7; break;
3331
                case 'U': remoteoffset =   8; break;
3332
                case 'V': remoteoffset =   9; break;
3333
                case 'W': remoteoffset =  10; break;
3334
                case 'X': remoteoffset =  11; break;
3335
                case 'Y': remoteoffset =  12; break;
3336
                default:  remoteoffset =   0; break;
3337
                }
3338
                remoteoffset = remoteoffset * 3600;
3339
        } else
3340
                return -1;
3341
3342
        return remoteoffset;
3343
}
3344
3345
time_t tzoffset_sec(time_t *now)
3346
{
3347
        struct tm gmt, *tmp, *lt;
3348
        gint off;
3349
3350
        tmp = gmtime(now);
3351
        g_return_val_if_fail(tmp != NULL, -1);
3352
        gmt = *tmp;
3353
        lt = localtime(now);
3354
        g_return_val_if_fail(lt != NULL, -1);
3355
3356
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3357
3358
        if (lt->tm_year < gmt.tm_year)
3359
                off -= 24 * 60;
3360
        else if (lt->tm_year > gmt.tm_year)
3361
                off += 24 * 60;
3362
        else if (lt->tm_yday < gmt.tm_yday)
3363
                off -= 24 * 60;
3364
        else if (lt->tm_yday > gmt.tm_yday)
3365
                off += 24 * 60;
3366
3367
        if (off >= 24 * 60)                /* should be impossible */
3368
                off = 23 * 60 + 59;        /* if not, insert silly value */
3369
        if (off <= -24 * 60)
3370
                off = -(23 * 60 + 59);
3371
3372
        return off * 60;
3373
}
3374
3375
/* calculate timezone offset */
3376
gchar *tzoffset(time_t *now)
3377
{
3378
        static gchar offset_string[6];
3379
        struct tm gmt, *tmp, *lt;
3380
        gint off;
3381
        gchar sign = '+';
3382
3383
        tmp = gmtime(now);
3384
        g_return_val_if_fail(tmp != NULL, NULL);
3385
        gmt = *tmp;
3386
        lt = localtime(now);
3387
        g_return_val_if_fail(lt != NULL, NULL);
3388
3389
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3390
3391
        if (lt->tm_year < gmt.tm_year)
3392
                off -= 24 * 60;
3393
        else if (lt->tm_year > gmt.tm_year)
3394
                off += 24 * 60;
3395
        else if (lt->tm_yday < gmt.tm_yday)
3396
                off -= 24 * 60;
3397
        else if (lt->tm_yday > gmt.tm_yday)
3398
                off += 24 * 60;
3399
3400
        if (off < 0) {
3401
                sign = '-';
3402
                off = -off;
3403
        }
3404
3405
        if (off >= 24 * 60)                /* should be impossible */
3406
                off = 23 * 60 + 59;        /* if not, insert silly value */
3407
3408
        sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3409
3410
        return offset_string;
3411
}
3412
3413
void get_rfc822_date(gchar *buf, gint len)
3414
{
3415
        struct tm *lt;
3416
        time_t t;
3417
        gchar day[4], mon[4];
3418
        gint dd, hh, mm, ss, yyyy;
3419
3420
        t = time(NULL);
3421
        lt = localtime(&t);
3422
3423
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
3424
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
3425
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3426
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3427
}
3428
3429
/* just a wrapper to suppress the warning of gcc about %c */
3430
size_t my_strftime(gchar *s, size_t max, const gchar *format,
3431
                   const struct tm *tm)
3432
{
3433
        return strftime(s, max, format, tm);
3434
}
3435
3436
/* UI hints */
3437
3438
static UIUpdateFunc ui_update_func = NULL;
3439
3440
void set_ui_update_func(UIUpdateFunc func)
3441
{
3442
        ui_update_func = func;
3443
}
3444
3445
void ui_update(void)
3446
{
3447
        if (ui_update_func)
3448
                ui_update_func();
3449
}
3450
3451
static ProgressFunc progress_func = NULL;
3452
3453
void set_progress_func(ProgressFunc func)
3454
{
3455
        progress_func = func;
3456
}
3457
3458
void progress_show(gint cur, gint total)
3459
{
3460
        if (progress_func)
3461
                progress_func(cur, total);
3462
}
3463
3464
/* user input */
3465
3466
static QueryPasswordFunc query_password_func = NULL;
3467
3468
void set_input_query_password_func(QueryPasswordFunc func)
3469
{
3470
        query_password_func = func;
3471
}
3472
3473
gchar *input_query_password(const gchar *server, const gchar *user)
3474
{
3475
        if (query_password_func)
3476
                return query_password_func(server, user);
3477
        else
3478
                return NULL;
3479
}
3480
3481
/* logging */
3482
3483
static FILE *log_fp = NULL;
3484
3485
void set_log_file(const gchar *filename)
3486
{
3487
        if (log_fp) return;
3488
        log_fp = g_fopen(filename, "wb");
3489
        if (!log_fp)
3490
                FILE_OP_ERROR(filename, "fopen");
3491
}
3492
3493
void close_log_file(void)
3494
{
3495
        if (log_fp) {
3496
                fclose(log_fp);
3497
                log_fp = NULL;
3498
        }
3499
}
3500
3501
static guint log_verbosity_count = 0;
3502
3503
void set_log_verbosity(gboolean verbose)
3504
{
3505
        if (verbose)
3506
                log_verbosity_count++;
3507
        else if (log_verbosity_count > 0)
3508
                log_verbosity_count--;
3509
}
3510
3511
gboolean get_debug_mode(void)
3512
{
3513
        return debug_mode;
3514
}
3515
3516
void set_debug_mode(gboolean enable)
3517
{
3518
        debug_mode = enable;
3519
}
3520
3521
static void log_dummy_func(const gchar *str)
3522
{
3523
}
3524
3525
static LogFunc log_print_ui_func = log_dummy_func;
3526
static LogFunc log_message_ui_func = log_dummy_func;
3527
static LogFunc log_warning_ui_func = log_dummy_func;
3528
static LogFunc log_error_ui_func = log_dummy_func;
3529
3530
static LogFunc log_show_status_func = log_dummy_func;
3531
3532
void set_log_ui_func(LogFunc print_func, LogFunc message_func,
3533
                     LogFunc warning_func, LogFunc error_func)
3534
{
3535
        log_print_ui_func = print_func;
3536
        log_message_ui_func = message_func;
3537
        log_warning_ui_func = warning_func;
3538
        log_error_ui_func = error_func;
3539
}
3540
3541
void set_log_show_status_func(LogFunc status_func)
3542
{
3543
        log_show_status_func = status_func;
3544
}
3545
3546
void debug_print(const gchar *format, ...)
3547
{
3548
        va_list args;
3549
        gchar buf[BUFFSIZE];
3550
3551
        if (!debug_mode) return;
3552
3553
        va_start(args, format);
3554
        g_vsnprintf(buf, sizeof(buf), format, args);
3555
        va_end(args);
3556
3557
        g_print("%s", buf);
3558
}
3559
3560
void status_print(const gchar *format, ...)
3561
{
3562
        va_list args;
3563
        gchar buf[BUFFSIZE];
3564
3565
        va_start(args, format);
3566
        g_vsnprintf(buf, sizeof(buf), format, args);
3567
        va_end(args);
3568
3569
        log_show_status_func(buf);
3570
}
3571
3572
#define TIME_LEN        11
3573
3574
void log_write(const gchar *str, const gchar *prefix)
3575
{
3576
        if (log_fp) {
3577
                gchar buf[TIME_LEN + 1];
3578
                time_t t;
3579
3580
                time(&t);
3581
                strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3582
3583
                fputs(buf, log_fp);
3584
                if (prefix)
3585
                        fputs(prefix, log_fp);
3586
                fputs(str, log_fp);
3587
                fflush(log_fp);
3588
        }
3589
}
3590
3591
void log_print(const gchar *format, ...)
3592
{
3593
        va_list args;
3594
        gchar buf[BUFFSIZE + TIME_LEN];
3595
        time_t t;
3596
3597
        time(&t);
3598
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3599
3600
        va_start(args, format);
3601
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3602
        va_end(args);
3603
3604
        if (debug_mode) fputs(buf, stdout);
3605
        log_print_ui_func(buf);
3606
        if (log_fp) {
3607
                fputs(buf, log_fp);
3608
                fflush(log_fp);
3609
        }
3610
        if (log_verbosity_count)
3611
                log_show_status_func(buf + TIME_LEN);
3612
}
3613
3614
void log_message(const gchar *format, ...)
3615
{
3616
        va_list args;
3617
        gchar buf[BUFFSIZE + TIME_LEN];
3618
        time_t t;
3619
3620
        time(&t);
3621
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3622
3623
        va_start(args, format);
3624
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3625
        va_end(args);
3626
3627
        if (debug_mode) g_message("%s", buf + TIME_LEN);
3628
        log_message_ui_func(buf + TIME_LEN);
3629
        if (log_fp) {
3630
                fwrite(buf, TIME_LEN, 1, log_fp);
3631
                fputs("* message: ", log_fp);
3632
                fputs(buf + TIME_LEN, log_fp);
3633
                fflush(log_fp);
3634
        }
3635
        log_show_status_func(buf + TIME_LEN);
3636
}
3637
3638
void log_warning(const gchar *format, ...)
3639
{
3640
        va_list args;
3641
        gchar buf[BUFFSIZE + TIME_LEN];
3642
        time_t t;
3643
3644
        time(&t);
3645
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3646
3647
        va_start(args, format);
3648
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3649
        va_end(args);
3650
3651
        g_warning("%s", buf);
3652
        log_warning_ui_func(buf + TIME_LEN);
3653
        if (log_fp) {
3654
                fwrite(buf, TIME_LEN, 1, log_fp);
3655
                fputs("** warning: ", log_fp);
3656
                fputs(buf + TIME_LEN, log_fp);
3657
                fflush(log_fp);
3658
        }
3659
}
3660
3661
void log_error(const gchar *format, ...)
3662
{
3663
        va_list args;
3664
        gchar buf[BUFFSIZE + TIME_LEN];
3665
        time_t t;
3666
3667
        time(&t);
3668
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
3669
3670
        va_start(args, format);
3671
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
3672
        va_end(args);
3673
3674
        g_warning("%s", buf);
3675
        log_error_ui_func(buf + TIME_LEN);
3676
        if (log_fp) {
3677
                fwrite(buf, TIME_LEN, 1, log_fp);
3678
                fputs("*** error: ", log_fp);
3679
                fputs(buf + TIME_LEN, log_fp);
3680
                fflush(log_fp);
3681
        }
3682
}