Statistics
| Revision:

root / libsylph / utils.c @ 3316

History | View | Annotate | Download (90.4 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2013 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
#ifndef WINVER
46
#  define WINVER 0x0500
47
#endif
48
#  include <windows.h>
49
#  include <wchar.h>
50
#  include <direct.h>
51
#  include <io.h>
52
#  include <shlobj.h>
53
#endif
54

    
55
#ifdef __APPLE__
56
#  include <AppKit/AppKit.h>
57
#endif
58

    
59
#include "utils.h"
60
#include "socket.h"
61

    
62
#define BUFFSIZE        8192
63

    
64
static gboolean debug_mode = FALSE;
65

    
66

    
67
#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
68
gint g_chdir(const gchar *path)
69
{
70
#ifdef G_OS_WIN32
71
        if (G_WIN32_HAVE_WIDECHAR_API()) {
72
                wchar_t *wpath;
73
                gint retval;
74
                gint save_errno;
75

    
76
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
77
                if (wpath == NULL) {
78
                        errno = EINVAL;
79
                        return -1;
80
                }
81

    
82
                retval = _wchdir(wpath);
83
                save_errno = errno;
84

    
85
                g_free(wpath);
86

    
87
                errno = save_errno;
88
                return retval;
89
        } else {
90
                gchar *cp_path;
91
                gint retval;
92
                gint save_errno;
93

    
94
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
95
                if (cp_path == NULL) {
96
                        errno = EINVAL;
97
                        return -1;
98
                }
99

    
100
                retval = chdir(cp_path);
101
                save_errno = errno;
102

    
103
                g_free(cp_path);
104

    
105
                errno = save_errno;
106
                return retval;
107
        }
108
#else
109
        return chdir(path);
110
#endif
111
}
112

    
113
gint g_chmod(const gchar *path, gint mode)
114
{
115
#ifdef G_OS_WIN32
116
        if (G_WIN32_HAVE_WIDECHAR_API()) {
117
                wchar_t *wpath;
118
                gint retval;
119
                gint save_errno;
120

    
121
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
122
                if (wpath == NULL) {
123
                        errno = EINVAL;
124
                        return -1;
125
                }
126

    
127
                retval = _wchmod(wpath, mode);
128
                save_errno = errno;
129

    
130
                g_free(wpath);
131

    
132
                errno = save_errno;
133
                return retval;
134
        } else {
135
                gchar *cp_path;
136
                gint retval;
137
                gint save_errno;
138

    
139
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
140
                if (cp_path == NULL) {
141
                        errno = EINVAL;
142
                        return -1;
143
                }
144

    
145
                retval = chmod(cp_path, mode);
146
                save_errno = errno;
147

    
148
                g_free(cp_path);
149

    
150
                errno = save_errno;
151
                return retval;
152
        }
153
#else
154
        return chmod(path, mode);
155
#endif
156
}
157
#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
158

    
159
#ifndef G_OS_UNIX
160
gint syl_link(const gchar *src, const gchar *dest)
161
{
162
#ifdef G_OS_WIN32
163
        wchar_t *wsrc;
164
        wchar_t *wdest;
165
        gint retval;
166
        gint save_errno;
167

    
168
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
169
        if (wsrc == NULL) {
170
                errno = EINVAL;
171
                return -1;
172
        }
173
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
174
        if (wdest == NULL) {
175
                g_free(wsrc);
176
                errno = EINVAL;
177
                return -1;
178
        }
179

    
180
        errno = 0;
181
        if (CreateHardLinkW(wdest, wsrc, NULL)) {
182
                retval = 0;
183
                /* debug_print("hard link created: %s -> %s\n", src, dest); */
184
        } else {
185
                retval = -1;
186
                switch (GetLastError()) {
187
                case ERROR_FILE_NOT_FOUND:
188
                case ERROR_PATH_NOT_FOUND:
189
                        errno = ENOENT; break;
190
                case ERROR_ACCESS_DENIED:
191
                case ERROR_LOCK_VIOLATION:
192
                case ERROR_SHARING_VIOLATION:
193
                        errno = EACCES; break;
194
                case ERROR_NOT_SAME_DEVICE:
195
                        errno = EXDEV; break;
196
                case ERROR_FILE_EXISTS:
197
                case ERROR_ALREADY_EXISTS:
198
                        errno = EEXIST; break;
199
                case ERROR_TOO_MANY_LINKS:
200
                        errno = EMLINK; break;
201
                default:
202
                        errno = EIO; break;
203
                }
204
        }
205
        save_errno = errno;
206

    
207
        g_free(wdest);
208
        g_free(wsrc);
209

    
210
        errno = save_errno;
211
        return retval;
212
#else
213
        return link(src, dest);
214
#endif
215
}
216
#endif /* !G_OS_UNIX */
217

    
218
void list_free_strings(GList *list)
219
{
220
        list = g_list_first(list);
221

    
222
        while (list != NULL) {
223
                g_free(list->data);
224
                list = list->next;
225
        }
226
}
227

    
228
void slist_free_strings(GSList *list)
229
{
230
        while (list != NULL) {
231
                g_free(list->data);
232
                list = list->next;
233
        }
234
}
235

    
236
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
237
{
238
        g_free(key);
239
}
240

    
241
void hash_free_strings(GHashTable *table)
242
{
243
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
244
}
245

    
246
static void hash_free_value_mem_func(gpointer key, gpointer value,
247
                                     gpointer data)
248
{
249
        g_free(value);
250
}
251

    
252
void hash_free_value_mem(GHashTable *table)
253
{
254
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
255
}
256

    
257
gint str_case_equal(gconstpointer v, gconstpointer v2)
258
{
259
        return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
260
}
261

    
262
guint str_case_hash(gconstpointer key)
263
{
264
        const gchar *p = key;
265
        guint h = *p;
266

    
267
        if (h) {
268
                h = g_ascii_tolower(h);
269
                for (p += 1; *p != '\0'; p++)
270
                        h = (h << 5) - h + g_ascii_tolower(*p);
271
        }
272

    
273
        return h;
274
}
275

    
276
void ptr_array_free_strings(GPtrArray *array)
277
{
278
        gint i;
279
        gchar *str;
280

    
281
        g_return_if_fail(array != NULL);
282

    
283
        for (i = 0; i < array->len; i++) {
284
                str = g_ptr_array_index(array, i);
285
                g_free(str);
286
        }
287
}
288

    
289
gboolean str_find(const gchar *haystack, const gchar *needle)
290
{
291
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
292
}
293

    
294
gboolean str_case_find(const gchar *haystack, const gchar *needle)
295
{
296
        return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
297
}
298

    
299
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
300
{
301
        return strcmp(haystack, needle) == 0;
302
}
303

    
304
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
305
{
306
        return g_ascii_strcasecmp(haystack, needle) == 0;
307
}
308

    
309
gint to_number(const gchar *nstr)
310
{
311
        register const gchar *p;
312

    
313
        if (*nstr == '\0') return -1;
314

    
315
        for (p = nstr; *p != '\0'; p++)
316
                if (!g_ascii_isdigit(*p)) return -1;
317

    
318
        return atoi(nstr);
319
}
320

    
321
guint to_unumber(const gchar *nstr)
322
{
323
        register const gchar *p;
324
        gulong val;
325

    
326
        if (*nstr == '\0') return 0;
327

    
328
        for (p = nstr; *p != '\0'; p++)
329
                if (!g_ascii_isdigit(*p)) return 0;
330

    
331
        errno = 0;
332
        val = strtoul(nstr, NULL, 10);
333
        if (val == ULONG_MAX && errno != 0)
334
                val = 0;
335

    
336
        return (guint)val;
337
}
338

    
339
/* convert integer into string,
340
   nstr must be not lower than 11 characters length */
341
gchar *itos_buf(gchar *nstr, gint n)
342
{
343
        g_snprintf(nstr, 11, "%d", n);
344
        return nstr;
345
}
346

    
347
/* convert integer into string */
348
gchar *itos(gint n)
349
{
350
        static gchar nstr[11];
351

    
352
        return itos_buf(nstr, n);
353
}
354

    
355
gchar *utos_buf(gchar *nstr, guint n)
356
{
357
        g_snprintf(nstr, 11, "%u", n);
358
        return nstr;
359
}
360

    
361
gchar *to_human_readable_buf(gchar *buf, size_t bufsize, gint64 size)
362
{
363
        if (size < 1024)
364
                g_snprintf(buf, bufsize, "%dB", (gint)size);
365
        else if ((size >> 10) < 1024)
366
                g_snprintf(buf, bufsize, "%.1fKB", (gfloat)size / (1 << 10));
367
        else if ((size >> 20) < 1024)
368
                g_snprintf(buf, bufsize, "%.2fMB", (gfloat)size / (1 << 20));
369
        else
370
                g_snprintf(buf, bufsize, "%.2fGB", (gfloat)size / (1 << 30));
371

    
372
        return buf;
373
}
374

    
375
gchar *to_human_readable(gint64 size)
376
{
377
        static gchar str[16];
378

    
379
        return to_human_readable_buf(str, sizeof(str), size);
380
}
381

    
382
/* strcmp with NULL-checking */
383
gint strcmp2(const gchar *s1, const gchar *s2)
384
{
385
        if (s1 == NULL || s2 == NULL)
386
                return -1;
387
        else
388
                return strcmp(s1, s2);
389
}
390

    
391
/* compare paths */
392
gint path_cmp(const gchar *s1, const gchar *s2)
393
{
394
        gint len1, len2;
395
#ifdef G_OS_WIN32
396
        gchar *s1_, *s2_;
397
#endif
398

    
399
        if (s1 == NULL || s2 == NULL) return -1;
400
        if (*s1 == '\0' || *s2 == '\0') return -1;
401

    
402
        len1 = strlen(s1);
403
        len2 = strlen(s2);
404

    
405
#ifdef G_OS_WIN32
406
        Xstrdup_a(s1_, s1, return -1);
407
        Xstrdup_a(s2_, s2, return -1);
408
        subst_char(s1_, '/', G_DIR_SEPARATOR);
409
        subst_char(s2_, '/', G_DIR_SEPARATOR);
410
        if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--;
411
        if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--;
412

    
413
        return strncmp(s1_, s2_, MAX(len1, len2));
414
#else
415
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
416
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
417

    
418
        return strncmp(s1, s2, MAX(len1, len2));
419
#endif
420
}
421

    
422
/* return TRUE if parent is equal to or ancestor of child */
423
gboolean is_path_parent(const gchar *parent, const gchar *child)
424
{
425
        gint plen;
426
        const gchar *base;
427

    
428
        g_return_val_if_fail(parent != NULL, FALSE);
429
        g_return_val_if_fail(child != NULL, FALSE);
430

    
431
        plen = strlen(parent);
432
        while (plen > 0 && G_IS_DIR_SEPARATOR(parent[plen - 1]))
433
                plen--;
434

    
435
#ifdef G_OS_WIN32
436
        if (!g_ascii_strncasecmp(parent, child, plen)) {
437
#else
438
        if (!strncmp(parent, child, plen)) {
439
#endif
440
                base = child + plen;
441
                if (!G_IS_DIR_SEPARATOR(*base) && *base != '\0')
442
                        return FALSE;
443
                return TRUE;
444
        }
445

    
446
        return FALSE;
447
}
448

    
449
/* remove trailing return code */
450
gchar *strretchomp(gchar *str)
451
{
452
        register gchar *s;
453

    
454
        if (!*str) return str;
455

    
456
        for (s = str + strlen(str) - 1;
457
             s >= str && (*s == '\n' || *s == '\r');
458
             s--)
459
                *s = '\0';
460

    
461
        return str;
462
}
463

    
464
/* remove trailing character */
465
gchar *strtailchomp(gchar *str, gchar tail_char)
466
{
467
        register gchar *s;
468

    
469
        if (!*str) return str;
470
        if (tail_char == '\0') return str;
471

    
472
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
473
                *s = '\0';
474

    
475
        return str;
476
}
477

    
478
/* remove CR (carriage return) */
479
gchar *strcrchomp(gchar *str)
480
{
481
        register gchar *s;
482

    
483
        if (!*str) return str;
484

    
485
        s = str + strlen(str) - 1;
486
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
487
                *(s - 1) = '\n';
488
                *s = '\0';
489
        }
490

    
491
        return str;
492
}
493

    
494
/* Similar to `strstr' but this function ignores the case of both strings.  */
495
gchar *strcasestr(const gchar *haystack, const gchar *needle)
496
{
497
        register size_t haystack_len, needle_len;
498

    
499
        haystack_len = strlen(haystack);
500
        needle_len   = strlen(needle);
501

    
502
        if (haystack_len < needle_len || needle_len == 0)
503
                return NULL;
504

    
505
        while (haystack_len >= needle_len) {
506
                if (!g_ascii_strncasecmp(haystack, needle, needle_len))
507
                        return (gchar *)haystack;
508
                else {
509
                        haystack++;
510
                        haystack_len--;
511
                }
512
        }
513

    
514
        return NULL;
515
}
516

    
517
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
518
                   gconstpointer needle, size_t needlelen)
519
{
520
        const gchar *haystack_ = (const gchar *)haystack;
521
        const gchar *needle_ = (const gchar *)needle;
522
        const gchar *haystack_cur = (const gchar *)haystack;
523
        size_t haystack_left = haystacklen;
524

    
525
        if (needlelen == 1)
526
                return memchr(haystack_, *needle_, haystacklen);
527

    
528
        while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
529
               != NULL) {
530
                if (haystacklen - (haystack_cur - haystack_) < needlelen)
531
                        break;
532
                if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
533
                        return (gpointer)haystack_cur;
534
                else {
535
                        haystack_cur++;
536
                        haystack_left = haystacklen - (haystack_cur - haystack_);
537
                }
538
        }
539

    
540
        return NULL;
541
}
542

    
543
/* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
544
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
545
{
546
        register const gchar *s = src;
547
        register gchar *d = dest;
548

    
549
        while (--n && *s)
550
                *d++ = *s++;
551
        *d = '\0';
552

    
553
        return dest;
554
}
555

    
556
/* Similar to g_str_has_suffix() but case-insensitive */
557
gboolean str_has_suffix_case(const gchar *str, const gchar *suffix)
558
{
559
        size_t len, s_len;
560

    
561
        if (!str || !suffix)
562
                return FALSE;
563

    
564
        len = strlen(str);
565
        s_len = strlen(suffix);
566

    
567
        if (s_len > len)
568
                return FALSE;
569

    
570
        return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0);
571
}
572

    
573
gint str_find_format_times(const gchar *haystack, gchar ch)
574
{
575
        gint n = 0;
576
        const gchar *p = haystack;
577

    
578
        while ((p = strchr(p, '%')) != NULL) {
579
                ++p;
580
                if (*p == '%') {
581
                        ++p;
582
                } else if (*p == ch) {
583
                        ++p;
584
                        ++n;
585
                } else
586
                        return -1;
587
        }
588

    
589
        return n;
590
}
591

    
592
/* Examine if next block is non-ASCII string */
593
gboolean is_next_nonascii(const gchar *s)
594
{
595
        const gchar *p;
596
        gboolean in_quote = FALSE;
597

    
598
        /* skip head space */
599
        for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
600
                ;
601
        while (*p != '\0') {
602
                if (!in_quote && g_ascii_isspace(*p))
603
                        break;
604
                if (*p == '"')
605
                        in_quote ^= TRUE;
606
                else if (*(guchar *)p > 127 || *(guchar *)p < 32)
607
                        return TRUE;
608
                ++p;
609
        }
610

    
611
        return FALSE;
612
}
613

    
614
gint get_next_word_len(const gchar *s)
615
{
616
        const gchar *p = s;
617
        gboolean in_quote = FALSE;
618

    
619
        while (*p != '\0') {
620
                if (!in_quote && g_ascii_isspace(*p))
621
                        break;
622
                if (*p == '"')
623
                        in_quote ^= TRUE;
624
                ++p;
625
        }
626

    
627
        return p - s;
628
}
629

    
630
/* compare subjects */
631
gint subject_compare(const gchar *s1, const gchar *s2)
632
{
633
        gchar *str1, *str2;
634

    
635
        if (!s1 || !s2) return -1;
636
        if (!*s1 || !*s2) return -1;
637

    
638
        Xstrdup_a(str1, s1, return -1);
639
        Xstrdup_a(str2, s2, return -1);
640

    
641
        trim_subject_for_compare(str1);
642
        trim_subject_for_compare(str2);
643

    
644
        if (!*str1 || !*str2) return -1;
645

    
646
        return strcmp(str1, str2);
647
}
648

    
649
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
650
{
651
        gchar *str1, *str2;
652

    
653
        if (!s1 || !s2) return -1;
654

    
655
        Xstrdup_a(str1, s1, return -1);
656
        Xstrdup_a(str2, s2, return -1);
657

    
658
        trim_subject_for_sort(str1);
659
        trim_subject_for_sort(str2);
660

    
661
        return g_ascii_strcasecmp(str1, str2);
662
}
663

    
664
void trim_subject_for_compare(gchar *str)
665
{
666
        gchar *srcp;
667

    
668
        eliminate_parenthesis(str, '[', ']');
669
        eliminate_parenthesis(str, '(', ')');
670
        g_strstrip(str);
671

    
672
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
673
                srcp = str + 3;
674
                while (g_ascii_isspace(*srcp)) srcp++;
675
                memmove(str, srcp, strlen(srcp) + 1);
676
        }
677
}
678

    
679
void trim_subject_for_sort(gchar *str)
680
{
681
        gchar *srcp;
682

    
683
        g_strstrip(str);
684

    
685
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
686
                srcp = str + 3;
687
                while (g_ascii_isspace(*srcp)) srcp++;
688
                memmove(str, srcp, strlen(srcp) + 1);
689
        }
690
}
691

    
692
void trim_subject(gchar *str)
693
{
694
        register gchar *srcp, *destp;
695
        gchar op, cl;
696
        gint in_brace;
697

    
698
        destp = str;
699
        while (!g_ascii_strncasecmp(destp, "Re:", 3)) {
700
                destp += 3;
701
                while (g_ascii_isspace(*destp)) destp++;
702
        }
703

    
704
        if (*destp == '[') {
705
                op = '[';
706
                cl = ']';
707
        } else if (*destp == '(') {
708
                op = '(';
709
                cl = ')';
710
        } else
711
                return;
712

    
713
        srcp = destp + 1;
714
        in_brace = 1;
715
        while (*srcp) {
716
                if (*srcp == op)
717
                        in_brace++;
718
                else if (*srcp == cl)
719
                        in_brace--;
720
                srcp++;
721
                if (in_brace == 0)
722
                        break;
723
        }
724
        while (g_ascii_isspace(*srcp)) srcp++;
725
        memmove(destp, srcp, strlen(srcp) + 1);
726
}
727

    
728
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
729
{
730
        register gchar *srcp, *destp;
731
        gint in_brace;
732

    
733
        srcp = destp = str;
734

    
735
        while ((destp = strchr(destp, op))) {
736
                in_brace = 1;
737
                srcp = destp + 1;
738
                while (*srcp) {
739
                        if (*srcp == op)
740
                                in_brace++;
741
                        else if (*srcp == cl)
742
                                in_brace--;
743
                        srcp++;
744
                        if (in_brace == 0)
745
                                break;
746
                }
747
                while (g_ascii_isspace(*srcp)) srcp++;
748
                memmove(destp, srcp, strlen(srcp) + 1);
749
        }
750
}
751

    
752
void extract_parenthesis(gchar *str, gchar op, gchar cl)
753
{
754
        register gchar *srcp, *destp;
755
        gint in_brace;
756

    
757
        srcp = destp = str;
758

    
759
        while ((srcp = strchr(destp, op))) {
760
                if (destp > str)
761
                        *destp++ = ' ';
762
                memmove(destp, srcp + 1, strlen(srcp));
763
                in_brace = 1;
764
                while(*destp) {
765
                        if (*destp == op)
766
                                in_brace++;
767
                        else if (*destp == cl)
768
                                in_brace--;
769

    
770
                        if (in_brace == 0)
771
                                break;
772

    
773
                        destp++;
774
                }
775
        }
776
        *destp = '\0';
777
}
778

    
779
void extract_parenthesis_with_escape(gchar *str, gchar op, gchar cl)
780
{
781
        register gchar *srcp, *destp;
782
        gint in_brace;
783

    
784
        srcp = destp = str;
785

    
786
        while ((srcp = strchr(srcp, op))) {
787
                if (destp > str)
788
                        *destp++ = ' ';
789
                ++srcp;
790
                in_brace = 1;
791
                while (*srcp) {
792
                        if (*srcp == op)
793
                                in_brace++;
794
                        else if (*srcp == cl)
795
                                in_brace--;
796

    
797
                        if (in_brace == 0)
798
                                break;
799

    
800
                        if (*srcp == '\\' && *(srcp + 1) != '\0')
801
                                ++srcp;
802

    
803
                        *destp++ = *srcp++;
804
                }
805
        }
806
        *destp = '\0';
807
}
808

    
809
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
810
                                         gchar op, gchar cl)
811
{
812
        register gchar *srcp, *destp;
813
        gint in_brace;
814
        gboolean in_quote = FALSE;
815

    
816
        srcp = destp = str;
817

    
818
        while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
819
                if (destp > str)
820
                        *destp++ = ' ';
821
                memmove(destp, srcp + 1, strlen(srcp));
822
                in_brace = 1;
823
                while(*destp) {
824
                        if (*destp == op && !in_quote)
825
                                in_brace++;
826
                        else if (*destp == cl && !in_quote)
827
                                in_brace--;
828
                        else if (*destp == quote_chr)
829
                                in_quote ^= TRUE;
830

    
831
                        if (in_brace == 0)
832
                                break;
833

    
834
                        destp++;
835
                }
836
        }
837
        *destp = '\0';
838
}
839

    
840
void eliminate_quote(gchar *str, gchar quote_chr)
841
{
842
        register gchar *srcp, *destp;
843

    
844
        srcp = destp = str;
845

    
846
        while ((destp = strchr(destp, quote_chr))) {
847
                if ((srcp = strchr(destp + 1, quote_chr))) {
848
                        srcp++;
849
                        while (g_ascii_isspace(*srcp)) srcp++;
850
                        memmove(destp, srcp, strlen(srcp) + 1);
851
                } else {
852
                        *destp = '\0';
853
                        break;
854
                }
855
        }
856
}
857

    
858
void extract_quote(gchar *str, gchar quote_chr)
859
{
860
        register gchar *p;
861

    
862
        if ((str = strchr(str, quote_chr))) {
863
                if ((p = strchr(str + 1, quote_chr))) {
864
                        *p = '\0';
865
                        memmove(str, str + 1, p - str);
866
                }
867
        }
868
}
869

    
870
void extract_quote_with_escape(gchar *str, gchar quote_chr)
871
{
872
        register gchar *sp, *dp;
873

    
874
        if ((sp = strchr(str, quote_chr))) {
875
                dp = sp;
876
                ++sp;
877
                while (*sp) {
878
                        if (*sp == quote_chr)
879
                                break;
880
                        else if (*sp == '\\' && *(sp + 1) != '\0')
881
                                ++sp;
882

    
883
                        *dp++ = *sp++;
884
                }
885
                *dp = '\0';
886
        }
887
}
888

    
889
void eliminate_address_comment(gchar *str)
890
{
891
        register gchar *srcp, *destp;
892
        gint in_brace;
893

    
894
        srcp = destp = str;
895

    
896
        while ((destp = strchr(destp, '"'))) {
897
                if ((srcp = strchr(destp + 1, '"'))) {
898
                        srcp++;
899
                        if (*srcp == '@') {
900
                                destp = srcp + 1;
901
                        } else {
902
                                while (g_ascii_isspace(*srcp)) srcp++;
903
                                memmove(destp, srcp, strlen(srcp) + 1);
904
                        }
905
                } else {
906
                        *destp = '\0';
907
                        break;
908
                }
909
        }
910

    
911
        srcp = destp = str;
912

    
913
        while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
914
                in_brace = 1;
915
                srcp = destp + 1;
916
                while (*srcp) {
917
                        if (*srcp == '(')
918
                                in_brace++;
919
                        else if (*srcp == ')')
920
                                in_brace--;
921
                        srcp++;
922
                        if (in_brace == 0)
923
                                break;
924
                }
925
                while (g_ascii_isspace(*srcp)) srcp++;
926
                memmove(destp, srcp, strlen(srcp) + 1);
927
        }
928
}
929

    
930
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
931
{
932
        gboolean in_quote = FALSE;
933

    
934
        while (*str) {
935
                if (*str == c && !in_quote)
936
                        return (gchar *)str;
937
                if (*str == quote_chr)
938
                        in_quote ^= TRUE;
939
                str++;
940
        }
941

    
942
        return NULL;
943
}
944

    
945
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
946
{
947
        gboolean in_quote = FALSE;
948
        const gchar *p;
949

    
950
        p = str + strlen(str) - 1;
951
        while (p >= str) {
952
                if (*p == c && !in_quote)
953
                        return (gchar *)p;
954
                if (*p == quote_chr)
955
                        in_quote ^= TRUE;
956
                p--;
957
        }
958

    
959
        return NULL;
960
}
961

    
962
void extract_address(gchar *str)
963
{
964
        eliminate_address_comment(str);
965
        if (strchr_with_skip_quote(str, '"', '<'))
966
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
967
        g_strstrip(str);
968
}
969

    
970
void extract_list_id_str(gchar *str)
971
{
972
        if (strchr_with_skip_quote(str, '"', '<'))
973
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
974
        g_strstrip(str);
975
}
976

    
977
gchar *extract_addresses(const gchar *str)
978
{
979
        GString *new_str;
980
        GSList *addr_list, *cur;
981

    
982
        if (!str)
983
                return NULL;
984

    
985
        addr_list = address_list_append(NULL, str);
986

    
987
        new_str = g_string_new(NULL);
988

    
989
        for (cur = addr_list; cur != NULL; cur = cur->next) {
990
                g_string_append(new_str, (gchar *)cur->data);
991
                if (cur->next)
992
                        g_string_append(new_str, ", ");
993
        }
994

    
995
        slist_free_strings(addr_list);
996
        g_slist_free(addr_list);
997

    
998
        return g_string_free(new_str, FALSE);
999
}
1000

    
1001
gchar *normalize_address_field(const gchar *str)
1002
{
1003
        GString *new_str;
1004
        GSList *addr_list, *cur;
1005
        gchar *addr, *p, *q, *r;
1006
        gchar *ret_str;
1007

    
1008
        addr_list = address_list_append_orig(NULL, str);
1009

    
1010
        new_str = g_string_new(NULL);
1011

    
1012
        for (cur = addr_list; cur != NULL; cur = cur->next) {
1013
                p = addr = (gchar *)cur->data;
1014
                q = strchr_with_skip_quote(p, '"', '<');
1015
                if (q && q > p) {
1016
                        r = q - 1;
1017
                        while (r > p && g_ascii_isspace(*r))
1018
                                --r;
1019
                        g_string_append_len(new_str, p, r - p + 1);
1020
                        g_string_append_c(new_str, ' ');
1021
                        p = q;
1022
                }
1023
                if (*p == '<') {
1024
                        q = strchr(p, '>');
1025
                        if (q) {
1026
                                r = q + 1;
1027
                                if (*r) {
1028
                                        while (g_ascii_isspace(*r))
1029
                                                ++r;
1030
                                        g_string_append(new_str, r);
1031
                                        if (new_str->len > 0 &&
1032
                                            !g_ascii_isspace
1033
                                                (new_str->str[new_str->len - 1]))
1034
                                                g_string_append_c(new_str, ' ');
1035
                                }
1036
                                g_string_append_len(new_str, p, q - p + 1);
1037
                        } else {
1038
                                g_string_append(new_str, p);
1039
                                g_string_append_c(new_str, '>');
1040
                        }
1041
                } else
1042
                        g_string_append(new_str, p);
1043

    
1044
                if (cur->next)
1045
                        g_string_append(new_str, ", ");
1046
        }
1047

    
1048
        slist_free_strings(addr_list);
1049
        ret_str = new_str->str;
1050
        g_string_free(new_str, FALSE);
1051

    
1052
        return ret_str;
1053
}
1054

    
1055
gboolean address_equal(const gchar *addr1, const gchar *addr2)
1056
{
1057
        gchar *addr1_, *addr2_;
1058

    
1059
        if (!addr1 || !addr2)
1060
                return FALSE;
1061

    
1062
        Xstrdup_a(addr1_, addr1, return FALSE);
1063
        Xstrdup_a(addr2_, addr2, return FALSE);
1064

    
1065
        extract_address(addr1_);
1066
        extract_address(addr2_);
1067

    
1068
        return strcmp(addr1_, addr2_) == 0;
1069
}
1070

    
1071
GSList *address_list_append_orig(GSList *addr_list, const gchar *str)
1072
{
1073
        const gchar *p = str, *q;
1074
        gchar *addr;
1075

    
1076
        if (!str) return addr_list;
1077

    
1078
        while (*p) {
1079
                if (*p == ',' || g_ascii_isspace(*p)) {
1080
                        ++p;
1081
                } else if ((q = strchr_with_skip_quote(p, '"', ','))) {
1082
                        addr = g_strndup(p, q - p);
1083
                        g_strstrip(addr);
1084
                        addr_list = g_slist_append(addr_list, addr);
1085
                        p = q + 1;
1086
                } else {
1087
                        addr = g_strdup(p);
1088
                        g_strstrip(addr);
1089
                        addr_list = g_slist_append(addr_list, addr);
1090
                        break;
1091
                }
1092
        }
1093

    
1094
        return addr_list;
1095
}
1096

    
1097
GSList *address_list_append(GSList *addr_list, const gchar *str)
1098
{
1099
        gchar *work;
1100
        gchar *workp;
1101

    
1102
        if (!str) return addr_list;
1103

    
1104
        Xstrdup_a(work, str, return addr_list);
1105

    
1106
        eliminate_address_comment(work);
1107
        workp = work;
1108

    
1109
        while (workp && *workp) {
1110
                gchar *p, *next;
1111

    
1112
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1113
                        *p = '\0';
1114
                        next = p + 1;
1115
                } else
1116
                        next = NULL;
1117

    
1118
                if (strchr_with_skip_quote(workp, '"', '<'))
1119
                        extract_parenthesis_with_skip_quote
1120
                                (workp, '"', '<', '>');
1121

    
1122
                g_strstrip(workp);
1123
                if (*workp)
1124
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
1125

    
1126
                workp = next;
1127
        }
1128

    
1129
        return addr_list;
1130
}
1131

    
1132
GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1133
{
1134
        const gchar *strp;
1135

    
1136
        if (!str) return msgid_list;
1137
        strp = str;
1138

    
1139
        while (strp && *strp) {
1140
                const gchar *start, *end;
1141
                gchar *msgid;
1142

    
1143
                if ((start = strchr(strp, '<')) != NULL) {
1144
                        end = strchr(start + 1, '>');
1145
                        if (!end) break;
1146
                } else
1147
                        break;
1148

    
1149
                msgid = g_strndup(start + 1, end - start - 1);
1150
                g_strstrip(msgid);
1151
                if (*msgid)
1152
                        msgid_list = g_slist_prepend(msgid_list, msgid);
1153
                else
1154
                        g_free(msgid);
1155

    
1156
                strp = end + 1;
1157
        }
1158

    
1159
        return msgid_list;
1160
}
1161

    
1162
GSList *references_list_append(GSList *msgid_list, const gchar *str)
1163
{
1164
        GSList *list;
1165

    
1166
        list = references_list_prepend(NULL, str);
1167
        list = g_slist_reverse(list);
1168
        msgid_list = g_slist_concat(msgid_list, list);
1169

    
1170
        return msgid_list;
1171
}
1172

    
1173
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1174
{
1175
        gchar *work;
1176
        gchar *workp;
1177

    
1178
        if (!str) return group_list;
1179

    
1180
        Xstrdup_a(work, str, return group_list);
1181

    
1182
        workp = work;
1183

    
1184
        while (workp && *workp) {
1185
                gchar *p, *next;
1186

    
1187
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1188
                        *p = '\0';
1189
                        next = p + 1;
1190
                } else
1191
                        next = NULL;
1192

    
1193
                g_strstrip(workp);
1194
                if (*workp)
1195
                        group_list = g_slist_append(group_list,
1196
                                                    g_strdup(workp));
1197

    
1198
                workp = next;
1199
        }
1200

    
1201
        return group_list;
1202
}
1203

    
1204
GList *add_history(GList *list, const gchar *str)
1205
{
1206
        GList *old;
1207

    
1208
        g_return_val_if_fail(str != NULL, list);
1209

    
1210
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1211
        if (old) {
1212
                g_free(old->data);
1213
                list = g_list_remove(list, old->data);
1214
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1215
                GList *last;
1216

    
1217
                last = g_list_last(list);
1218
                if (last) {
1219
                        g_free(last->data);
1220
                        list = g_list_remove(list, last->data);
1221
                }
1222
        }
1223

    
1224
        list = g_list_prepend(list, g_strdup(str));
1225

    
1226
        return list;
1227
}
1228

    
1229
void remove_return(gchar *str)
1230
{
1231
        register gchar *p = str;
1232

    
1233
        while (*p) {
1234
                if (*p == '\n' || *p == '\r')
1235
                        memmove(p, p + 1, strlen(p));
1236
                else
1237
                        p++;
1238
        }
1239
}
1240

    
1241
void remove_space(gchar *str)
1242
{
1243
        register gchar *p = str;
1244
        register gint spc;
1245

    
1246
        while (*p) {
1247
                spc = 0;
1248
                while (g_ascii_isspace(*(p + spc)))
1249
                        spc++;
1250
                if (spc)
1251
                        memmove(p, p + spc, strlen(p + spc) + 1);
1252
                else
1253
                        p++;
1254
        }
1255
}
1256

    
1257
void unfold_line(gchar *str)
1258
{
1259
        register gchar *p = str;
1260
        register gint spc;
1261

    
1262
        while (*p) {
1263
                if (*p == '\n' || *p == '\r') {
1264
                        *p++ = ' ';
1265
                        spc = 0;
1266
                        while (g_ascii_isspace(*(p + spc)))
1267
                                spc++;
1268
                        if (spc)
1269
                                memmove(p, p + spc, strlen(p + spc) + 1);
1270
                } else
1271
                        p++;
1272
        }
1273
}
1274

    
1275
void subst_char(gchar *str, gchar orig, gchar subst)
1276
{
1277
        register gchar *p = str;
1278

    
1279
        while (*p) {
1280
                if (*p == orig)
1281
                        *p = subst;
1282
                p++;
1283
        }
1284
}
1285

    
1286
void subst_chars(gchar *str, gchar *orig, gchar subst)
1287
{
1288
        register gchar *p = str;
1289

    
1290
        while (*p) {
1291
                if (strchr(orig, *p) != NULL)
1292
                        *p = subst;
1293
                ++p;
1294
        }
1295
}
1296

    
1297
void subst_null(gchar *str, gint len, gchar subst)
1298
{
1299
        register gchar *p = str;
1300

    
1301
        while (len--) {
1302
                if (*p == '\0')
1303
                        *p = subst;
1304
                ++p;
1305
        }
1306
}
1307

    
1308
void subst_control(gchar *str, gchar subst)
1309
{
1310
        register gchar *p = str;
1311

    
1312
        while (*p) {
1313
                if (g_ascii_iscntrl(*p))
1314
                        *p = subst;
1315
                ++p;
1316
        }
1317
}
1318

    
1319
void subst_for_filename(gchar *str)
1320
{
1321
        subst_chars(str, " \t\r\n\"'\\/:;*?<>|", '_');
1322
}
1323

    
1324
gchar *get_alt_filename(const gchar *filename, gint count)
1325
{
1326
        const gchar *ext;
1327
        gchar *alt_filename;
1328

    
1329
        ext = strrchr(filename, '.');
1330

    
1331
        if (ext) {
1332
                gchar *base;
1333

    
1334
                base = g_strndup(filename, ext - filename);
1335
                alt_filename = g_strdup_printf("%s-%d%s", base, count, ext);
1336
                g_free(base);
1337
        } else
1338
                alt_filename = g_strdup_printf("%s-%d", filename, count);
1339

    
1340
        return alt_filename;
1341
}
1342

    
1343
gboolean is_header_line(const gchar *str)
1344
{
1345
        if (str[0] == ':') return FALSE;
1346

    
1347
        while (*str != '\0' && *str != ' ') {
1348
                if (*str == ':')
1349
                        return TRUE;
1350
                str++;
1351
        }
1352

    
1353
        return FALSE;
1354
}
1355

    
1356
gboolean is_ascii_str(const gchar *str)
1357
{
1358
        const guchar *p = (const guchar *)str;
1359

    
1360
        while (*p != '\0') {
1361
                if (*p != '\t' && *p != ' ' &&
1362
                    *p != '\r' && *p != '\n' &&
1363
                    (*p < 32 || *p >= 127))
1364
                        return FALSE;
1365
                p++;
1366
        }
1367

    
1368
        return TRUE;
1369
}
1370

    
1371
gint get_quote_level(const gchar *str)
1372
{
1373
        const gchar *first_pos;
1374
        const gchar *last_pos;
1375
        const gchar *p = str;
1376
        gint quote_level = -1;
1377

    
1378
        /* speed up line processing by only searching to the last '>' */
1379
        if ((first_pos = strchr(str, '>')) != NULL) {
1380
                /* skip a line if it contains a '<' before the initial '>' */
1381
                if (memchr(str, '<', first_pos - str) != NULL)
1382
                        return -1;
1383
                last_pos = strrchr(first_pos, '>');
1384
        } else
1385
                return -1;
1386

    
1387
        while (p <= last_pos) {
1388
                while (p < last_pos) {
1389
                        if (g_ascii_isspace(*p))
1390
                                p++;
1391
                        else
1392
                                break;
1393
                }
1394

    
1395
                if (*p == '>')
1396
                        quote_level++;
1397
                else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1398
                        /* any characters are allowed except '-' and space */
1399
                        while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) &&
1400
                               p < last_pos)
1401
                                p++;
1402
                        if (*p == '>')
1403
                                quote_level++;
1404
                        else
1405
                                break;
1406
                }
1407

    
1408
                p++;
1409
        }
1410

    
1411
        return quote_level;
1412
}
1413

    
1414
gint check_line_length(const gchar *str, gint max_chars, gint *line)
1415
{
1416
        const gchar *p = str, *q;
1417
        gint cur_line = 0, len;
1418

    
1419
        while ((q = strchr(p, '\n')) != NULL) {
1420
                len = q - p + 1;
1421
                if (len > max_chars) {
1422
                        if (line)
1423
                                *line = cur_line;
1424
                        return -1;
1425
                }
1426
                p = q + 1;
1427
                ++cur_line;
1428
        }
1429

    
1430
        len = strlen(p);
1431
        if (len > max_chars) {
1432
                if (line)
1433
                        *line = cur_line;
1434
                return -1;
1435
        }
1436

    
1437
        return 0;
1438
}
1439

    
1440
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1441
{
1442
        register guint haystack_len, needle_len;
1443
        gboolean in_squote = FALSE, in_dquote = FALSE;
1444

    
1445
        haystack_len = strlen(haystack);
1446
        needle_len   = strlen(needle);
1447

    
1448
        if (haystack_len < needle_len || needle_len == 0)
1449
                return NULL;
1450

    
1451
        while (haystack_len >= needle_len) {
1452
                if (!in_squote && !in_dquote &&
1453
                    !strncmp(haystack, needle, needle_len))
1454
                        return (gchar *)haystack;
1455

    
1456
                /* 'foo"bar"' -> foo"bar"
1457
                   "foo'bar'" -> foo'bar' */
1458
                if (*haystack == '\'') {
1459
                        if (in_squote)
1460
                                in_squote = FALSE;
1461
                        else if (!in_dquote)
1462
                                in_squote = TRUE;
1463
                } else if (*haystack == '\"') {
1464
                        if (in_dquote)
1465
                                in_dquote = FALSE;
1466
                        else if (!in_squote)
1467
                                in_dquote = TRUE;
1468
                }
1469

    
1470
                haystack++;
1471
                haystack_len--;
1472
        }
1473

    
1474
        return NULL;
1475
}
1476

    
1477
gchar *strcasestr_with_skip_quote(const gchar *haystack, const gchar *needle)
1478
{
1479
        register guint haystack_len, needle_len;
1480
        gboolean in_squote = FALSE, in_dquote = FALSE;
1481

    
1482
        haystack_len = strlen(haystack);
1483
        needle_len   = strlen(needle);
1484

    
1485
        if (haystack_len < needle_len || needle_len == 0)
1486
                return NULL;
1487

    
1488
        while (haystack_len >= needle_len) {
1489
                if (!in_squote && !in_dquote &&
1490
                    !g_ascii_strncasecmp(haystack, needle, needle_len))
1491
                        return (gchar *)haystack;
1492

    
1493
                /* 'foo"bar"' -> foo"bar"
1494
                   "foo'bar'" -> foo'bar' */
1495
                if (*haystack == '\'') {
1496
                        if (in_squote)
1497
                                in_squote = FALSE;
1498
                        else if (!in_dquote)
1499
                                in_squote = TRUE;
1500
                } else if (*haystack == '\"') {
1501
                        if (in_dquote)
1502
                                in_dquote = FALSE;
1503
                        else if (!in_squote)
1504
                                in_dquote = TRUE;
1505
                }
1506

    
1507
                haystack++;
1508
                haystack_len--;
1509
        }
1510

    
1511
        return NULL;
1512
}
1513

    
1514
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1515
{
1516
        const gchar *p;
1517
        gchar quote_chr = '"';
1518
        gint in_brace;
1519
        gboolean in_quote = FALSE;
1520

    
1521
        p = str;
1522

    
1523
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1524
                p++;
1525
                in_brace = 1;
1526
                while (*p) {
1527
                        if (*p == op && !in_quote)
1528
                                in_brace++;
1529
                        else if (*p == cl && !in_quote)
1530
                                in_brace--;
1531
                        else if (*p == quote_chr)
1532
                                in_quote ^= TRUE;
1533

    
1534
                        if (in_brace == 0)
1535
                                return (gchar *)p;
1536

    
1537
                        p++;
1538
                }
1539
        }
1540

    
1541
        return NULL;
1542
}
1543

    
1544
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1545
                             gint max_tokens)
1546
{
1547
        GSList *string_list = NULL, *slist;
1548
        gchar **str_array;
1549
        const gchar *s_op, *s_cl;
1550
        guint i, n = 1;
1551

    
1552
        g_return_val_if_fail(str != NULL, NULL);
1553

    
1554
        if (max_tokens < 1)
1555
                max_tokens = G_MAXINT;
1556

    
1557
        s_op = strchr_with_skip_quote(str, '"', op);
1558
        if (!s_op) return NULL;
1559
        str = s_op;
1560
        s_cl = strchr_parenthesis_close(str, op, cl);
1561
        if (s_cl) {
1562
                do {
1563
                        guint len;
1564
                        gchar *new_string;
1565

    
1566
                        str++;
1567
                        len = s_cl - str;
1568
                        new_string = g_new(gchar, len + 1);
1569
                        strncpy(new_string, str, len);
1570
                        new_string[len] = 0;
1571
                        string_list = g_slist_prepend(string_list, new_string);
1572
                        n++;
1573
                        str = s_cl + 1;
1574

    
1575
                        while (*str && g_ascii_isspace(*str)) str++;
1576
                        if (*str != op) {
1577
                                string_list = g_slist_prepend(string_list,
1578
                                                              g_strdup(""));
1579
                                n++;
1580
                                s_op = strchr_with_skip_quote(str, '"', op);
1581
                                if (!--max_tokens || !s_op) break;
1582
                                str = s_op;
1583
                        } else
1584
                                s_op = str;
1585
                        s_cl = strchr_parenthesis_close(str, op, cl);
1586
                } while (--max_tokens && s_cl);
1587
        }
1588

    
1589
        str_array = g_new(gchar*, n);
1590

    
1591
        i = n - 1;
1592
        str_array[i--] = NULL;
1593
        for (slist = string_list; slist; slist = slist->next)
1594
                str_array[i--] = slist->data;
1595

    
1596
        g_slist_free(string_list);
1597

    
1598
        return str_array;
1599
}
1600

    
1601
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1602
                            gint max_tokens)
1603
{
1604
        GSList *string_list = NULL, *slist;
1605
        gchar **str_array, *s, *new_str;
1606
        guint i, n = 1, len;
1607

    
1608
        g_return_val_if_fail(str != NULL, NULL);
1609
        g_return_val_if_fail(delim != NULL, NULL);
1610

    
1611
        if (max_tokens < 1)
1612
                max_tokens = G_MAXINT;
1613

    
1614
        s = strstr_with_skip_quote(str, delim);
1615
        if (s) {
1616
                guint delimiter_len = strlen(delim);
1617

    
1618
                do {
1619
                        len = s - str;
1620
                        new_str = g_strndup(str, len);
1621

    
1622
                        if (new_str[0] == '\'' || new_str[0] == '\"') {
1623
                                if (new_str[len - 1] == new_str[0]) {
1624
                                        new_str[len - 1] = '\0';
1625
                                        memmove(new_str, new_str + 1, len - 1);
1626
                                }
1627
                        }
1628
                        string_list = g_slist_prepend(string_list, new_str);
1629
                        n++;
1630
                        str = s + delimiter_len;
1631
                        s = strstr_with_skip_quote(str, delim);
1632
                } while (--max_tokens && s);
1633
        }
1634

    
1635
        if (*str) {
1636
                new_str = g_strdup(str);
1637
                if (new_str[0] == '\'' || new_str[0] == '\"') {
1638
                        len = strlen(str);
1639
                        if (new_str[len - 1] == new_str[0]) {
1640
                                new_str[len - 1] = '\0';
1641
                                memmove(new_str, new_str + 1, len - 1);
1642
                        }
1643
                }
1644
                string_list = g_slist_prepend(string_list, new_str);
1645
                n++;
1646
        }
1647

    
1648
        str_array = g_new(gchar*, n);
1649

    
1650
        i = n - 1;
1651
        str_array[i--] = NULL;
1652
        for (slist = string_list; slist; slist = slist->next)
1653
                str_array[i--] = slist->data;
1654

    
1655
        g_slist_free(string_list);
1656

    
1657
        return str_array;
1658
}
1659

    
1660
gchar **strsplit_csv(const gchar *str, gchar delim, gint max_tokens)
1661
{
1662
        GSList *string_list = NULL, *slist;
1663
        gchar **str_array, *s, *new_str;
1664
        gchar *tmp, *tmpp, *p;
1665
        guint i, n = 1, len;
1666

    
1667
        g_return_val_if_fail(str != NULL, NULL);
1668

    
1669
        if (max_tokens < 1)
1670
                max_tokens = G_MAXINT;
1671

    
1672
        s = strchr_with_skip_quote(str, '"', delim);
1673
        if (s) {
1674
                do {
1675
                        len = s - str;
1676
                        tmpp = tmp = g_strndup(str, len);
1677

    
1678
                        if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1679
                                tmp[len - 1] = '\0';
1680
                                ++tmpp;
1681
                                p = new_str = g_malloc(len - 1);
1682
                                while (*tmpp) {
1683
                                        if (*tmpp == '"' && *(tmpp + 1) == '"')
1684
                                                ++tmpp;
1685
                                        *p++ = *tmpp++;
1686
                                }
1687
                                *p = '\0';
1688
                                g_free(tmp);
1689
                        } else
1690
                                new_str = tmp;
1691

    
1692
                        string_list = g_slist_prepend(string_list, new_str);
1693
                        n++;
1694
                        str = s + 1;
1695
                        s = strchr_with_skip_quote(str, '"', delim);
1696
                } while (--max_tokens && s);
1697
        }
1698

    
1699
        if (*str) {
1700
                len = strlen(str);
1701
                tmpp = tmp = g_strdup(str);
1702

    
1703
                if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) {
1704
                        tmp[len - 1] = '\0';
1705
                        ++tmpp;
1706
                        p = new_str = g_malloc(len - 1);
1707
                        while (*tmpp) {
1708
                                if (*tmpp == '"' && *(tmpp + 1) == '"')
1709
                                        ++tmpp;
1710
                                *p++ = *tmpp++;
1711
                        }
1712
                        *p = '\0';
1713
                        g_free(tmp);
1714
                } else
1715
                        new_str = tmp;
1716

    
1717
                string_list = g_slist_prepend(string_list, new_str);
1718
                n++;
1719
        }
1720

    
1721
        str_array = g_new(gchar*, n);
1722

    
1723
        i = n - 1;
1724
        str_array[i--] = NULL;
1725
        for (slist = string_list; slist; slist = slist->next)
1726
                str_array[i--] = slist->data;
1727

    
1728
        g_slist_free(string_list);
1729

    
1730
        return str_array;
1731
}
1732

    
1733
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1734
{
1735
        gchar *abbrev_group;
1736
        gchar *ap;
1737
        const gchar *p = group;
1738
        const gchar *last;
1739

    
1740
        last = group + strlen(group);
1741
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1742

    
1743
        while (*p) {
1744
                while (*p == '.')
1745
                        *ap++ = *p++;
1746
                if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1747
                        *ap++ = *p++;
1748
                        while (*p != '.') p++;
1749
                } else {
1750
                        strcpy(ap, p);
1751
                        return abbrev_group;
1752
                }
1753
        }
1754

    
1755
        *ap = '\0';
1756
        return abbrev_group;
1757
}
1758

    
1759
gchar *trim_string(const gchar *str, gint len)
1760
{
1761
        const gchar *p = str;
1762
        gint mb_len;
1763
        gchar *new_str;
1764
        gint new_len = 0;
1765

    
1766
        if (!str) return NULL;
1767
        if (strlen(str) <= len)
1768
                return g_strdup(str);
1769
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1770
                return g_strdup(str);
1771

    
1772
        while (*p != '\0') {
1773
                mb_len = g_utf8_skip[*(guchar *)p];
1774
                if (mb_len == 0)
1775
                        break;
1776
                else if (new_len + mb_len > len)
1777
                        break;
1778

    
1779
                new_len += mb_len;
1780
                p += mb_len;
1781
        }
1782

    
1783
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1784
        return g_strconcat(new_str, "...", NULL);
1785
}
1786

    
1787
gchar *trim_string_before(const gchar *str, gint len)
1788
{
1789
        const gchar *p = str;
1790
        gint mb_len;
1791
        gint new_len;
1792

    
1793
        if (!str) return NULL;
1794
        if ((new_len = strlen(str)) <= len)
1795
                return g_strdup(str);
1796
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1797
                return g_strdup(str);
1798

    
1799
        while (*p != '\0') {
1800
                mb_len = g_utf8_skip[*(guchar *)p];
1801
                if (mb_len == 0)
1802
                        break;
1803

    
1804
                new_len -= mb_len;
1805
                p += mb_len;
1806

    
1807
                if (new_len <= len)
1808
                        break;
1809
        }
1810

    
1811
        return g_strconcat("...", p, NULL);
1812
}
1813

    
1814
GList *uri_list_extract_filenames(const gchar *uri_list)
1815
{
1816
        GList *result = NULL;
1817
        gchar *file;
1818

    
1819
#if GLIB_CHECK_VERSION(2, 6, 0)
1820
        gchar **uris;
1821
        gint i;
1822

    
1823
        uris = g_uri_list_extract_uris(uri_list);
1824
        g_return_val_if_fail(uris != NULL, NULL);
1825

    
1826
        for (i = 0; uris[i] != NULL; i++) {
1827
                file = g_filename_from_uri(uris[i], NULL, NULL);
1828
                if (file)
1829
                        result = g_list_append(result, file);
1830
        }
1831

    
1832
        g_strfreev(uris);
1833

    
1834
        return result;
1835
#else
1836
        const gchar *p, *q;
1837

    
1838
        p = uri_list;
1839

    
1840
        while (p) {
1841
                if (*p != '#') {
1842
                        while (g_ascii_isspace(*p)) p++;
1843
                        if (!strncmp(p, "file:", 5)) {
1844
                                p += 5;
1845
                                while (*p == '/' && *(p + 1) == '/') p++;
1846
                                q = p;
1847
                                while (*q && *q != '\n' && *q != '\r') q++;
1848

    
1849
                                if (q > p) {
1850
                                        q--;
1851
                                        while (q > p && g_ascii_isspace(*q))
1852
                                                q--;
1853
                                        file = g_malloc(q - p + 2);
1854
                                        strncpy(file, p, q - p + 1);
1855
                                        file[q - p + 1] = '\0';
1856
                                        decode_uri(file, file);
1857
                                        result = g_list_append(result, file);
1858
                                }
1859
                        }
1860
                }
1861
                p = strchr(p, '\n');
1862
                if (p) p++;
1863
        }
1864

    
1865
        return result;
1866
#endif
1867
}
1868

    
1869
#define HEX_TO_INT(val, hex) \
1870
{ \
1871
        gchar c = hex; \
1872
 \
1873
        if ('0' <= c && c <= '9') { \
1874
                val = c - '0'; \
1875
        } else if ('a' <= c && c <= 'f') { \
1876
                val = c - 'a' + 10; \
1877
        } else if ('A' <= c && c <= 'F') { \
1878
                val = c - 'A' + 10; \
1879
        } else { \
1880
                val = 0; \
1881
        } \
1882
}
1883

    
1884
#define INT_TO_HEX(hex, val)                \
1885
{                                        \
1886
        if ((val) < 10)                        \
1887
                hex = '0' + (val);        \
1888
        else                                \
1889
                hex = 'a' + (val) - 10;        \
1890
}
1891

    
1892
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1893
 * characters.
1894
 */
1895
static gint axtoi(const gchar *hex_str)
1896
{
1897
        gint hi, lo;
1898

    
1899
        HEX_TO_INT(hi, hex_str[0]);
1900
        HEX_TO_INT(lo, hex_str[1]);
1901

    
1902
        return (hi << 4) + lo;
1903
}
1904

    
1905
static void get_hex_str(gchar *out, guchar ch)
1906
{
1907
        gchar hex;
1908

    
1909
        INT_TO_HEX(hex, ch >> 4);
1910
        *out++ = hex;
1911
        INT_TO_HEX(hex, ch & 0x0f);
1912
        *out++ = hex;
1913
}
1914

    
1915
gboolean is_uri_string(const gchar *str)
1916
{
1917
        return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1918
                g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1919
                g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1920
                g_ascii_strncasecmp(str, "www.", 4) == 0);
1921
}
1922

    
1923
gchar *get_uri_path(const gchar *uri)
1924
{
1925
        if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1926
                return (gchar *)(uri + 7);
1927
        else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1928
                return (gchar *)(uri + 8);
1929
        else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1930
                return (gchar *)(uri + 6);
1931
        else
1932
                return (gchar *)uri;
1933
}
1934

    
1935
gint get_uri_len(const gchar *str)
1936
{
1937
        const gchar *p;
1938

    
1939
        if (is_uri_string(str)) {
1940
                for (p = str; *p != '\0'; p++) {
1941
                        if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1942
                                break;
1943
                }
1944
                return p - str;
1945
        }
1946

    
1947
        return 0;
1948
}
1949

    
1950
/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1951
 * plusses, and escape characters are used)
1952
 * Note: decoded_uri and encoded_uri can point the same location
1953
 */
1954
void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1955
{
1956
        gchar *dec = decoded_uri;
1957
        const gchar *enc = encoded_uri;
1958

    
1959
        while (*enc) {
1960
                if (*enc == '%') {
1961
                        enc++;
1962
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1963
                            g_ascii_isxdigit((guchar)enc[1])) {
1964
                                *dec = axtoi(enc);
1965
                                dec++;
1966
                                enc += 2;
1967
                        }
1968
                } else {
1969
                        if (*enc == '+')
1970
                                *dec = ' ';
1971
                        else
1972
                                *dec = *enc;
1973
                        dec++;
1974
                        enc++;
1975
                }
1976
        }
1977

    
1978
        *dec = '\0';
1979
}
1980

    
1981
void decode_xdigit_encoded_str(gchar *decoded, const gchar *encoded)
1982
{
1983
        gchar *dec = decoded;
1984
        const gchar *enc = encoded;
1985

    
1986
        while (*enc) {
1987
                if (*enc == '%') {
1988
                        enc++;
1989
                        if (g_ascii_isxdigit((guchar)enc[0]) &&
1990
                            g_ascii_isxdigit((guchar)enc[1])) {
1991
                                *dec++ = axtoi(enc);
1992
                                enc += 2;
1993
                        }
1994
                } else
1995
                        *dec++ = *enc++;
1996
        }
1997

    
1998
        *dec = '\0';
1999
}
2000

    
2001
gchar *encode_uri(const gchar *filename)
2002
{
2003
        gchar *uri;
2004

    
2005
        uri = g_filename_to_uri(filename, NULL, NULL);
2006
        if (!uri)
2007
                uri = g_strconcat("file://", filename, NULL);
2008

    
2009
        return uri;
2010
}
2011

    
2012
gchar *uriencode_for_filename(const gchar *filename)
2013
{
2014
        const gchar *p = filename;
2015
        gchar *enc, *outp;
2016

    
2017
        outp = enc = g_malloc(strlen(filename) * 3 + 1);
2018

    
2019
        for (p = filename; *p != '\0'; p++) {
2020
                if (strchr("\t\r\n\"'\\/:;*?<>|", *p)) {
2021
                        *outp++ = '%';
2022
                        get_hex_str(outp, *p);
2023
                        outp += 2;
2024
                } else
2025
                        *outp++ = *p;
2026
        }
2027

    
2028
        *outp = '\0';
2029
        return enc;
2030
}
2031

    
2032
gchar *uriencode_for_mailto(const gchar *mailto)
2033
{
2034
        const gchar *p = mailto;
2035
        gchar *enc, *outp;
2036

    
2037
        outp = enc = g_malloc(strlen(mailto) * 3 + 1);
2038

    
2039
        for (p = mailto; *p != '\0'; p++) {
2040
                if (*p == '+') {
2041
                        *outp++ = '%';
2042
                        get_hex_str(outp, *p);
2043
                        outp += 2;
2044
                } else
2045
                        *outp++ = *p;
2046
        }
2047

    
2048
        *outp = '\0';
2049
        return enc;
2050
}
2051

    
2052
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
2053
                     gchar **subject, gchar **inreplyto, gchar **body)
2054
{
2055
        gchar *tmp_mailto;
2056
        gchar *p;
2057

    
2058
        Xstrdup_a(tmp_mailto, mailto, return -1);
2059

    
2060
        if (!strncmp(tmp_mailto, "mailto:", 7))
2061
                tmp_mailto += 7;
2062

    
2063
        p = strchr(tmp_mailto, '?');
2064
        if (p) {
2065
                *p = '\0';
2066
                p++;
2067
        }
2068

    
2069
        if (to && !*to) {
2070
                *to = g_malloc(strlen(tmp_mailto) + 1);
2071
                decode_uri(*to, tmp_mailto);
2072
        }
2073

    
2074
        while (p) {
2075
                gchar *field, *value;
2076

    
2077
                field = p;
2078

    
2079
                p = strchr(p, '=');
2080
                if (!p) break;
2081
                *p = '\0';
2082
                p++;
2083

    
2084
                value = p;
2085

    
2086
                p = strchr(p, '&');
2087
                if (p) {
2088
                        *p = '\0';
2089
                        p++;
2090
                }
2091

    
2092
                if (*value == '\0') continue;
2093

    
2094
                if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
2095
                        *cc = g_malloc(strlen(value) + 1);
2096
                        decode_uri(*cc, value);
2097
                } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
2098
                        *bcc = g_malloc(strlen(value) + 1);
2099
                        decode_uri(*bcc, value);
2100
                } else if (subject && !*subject &&
2101
                           !g_ascii_strcasecmp(field, "subject")) {
2102
                        *subject = g_malloc(strlen(value) + 1);
2103
                        decode_uri(*subject, value);
2104
                } else if (inreplyto && !*inreplyto &&
2105
                           !g_ascii_strcasecmp(field, "in-reply-to")) {
2106
                        *inreplyto = g_malloc(strlen(value) + 1);
2107
                        decode_uri(*inreplyto, value);
2108
                } else if (body && !*body &&
2109
                           !g_ascii_strcasecmp(field, "body")) {
2110
                        *body = g_malloc(strlen(value) + 1);
2111
                        decode_uri(*body, value);
2112
                }
2113
        }
2114

    
2115
        return 0;
2116
}
2117

    
2118
static gchar *startup_dir = NULL;
2119
static gchar *rc_dir = NULL;
2120

    
2121
void set_startup_dir(void)
2122
{
2123
#ifdef G_OS_WIN32
2124
        if (!startup_dir) {
2125
                startup_dir = g_win32_get_package_installation_directory
2126
                        (NULL, NULL);
2127
                if (startup_dir) {
2128
                        if (g_chdir(startup_dir) < 0) {
2129
                                FILE_OP_ERROR(startup_dir, "chdir");
2130
                                g_free(startup_dir);
2131
                                startup_dir = g_get_current_dir();
2132
                        }
2133
                } else
2134
                        startup_dir = g_get_current_dir();
2135
        }
2136
#elif defined(__APPLE__)
2137
        if (!startup_dir) {
2138
                const gchar *path;
2139
                NSAutoreleasePool *pool;
2140

    
2141
                pool = [[NSAutoreleasePool alloc] init];
2142

    
2143
                path = [[[NSBundle mainBundle] bundlePath] UTF8String];
2144
                startup_dir = g_strdup(path);
2145

    
2146
                [pool release];
2147

    
2148
                if (!startup_dir)
2149
                        startup_dir = g_get_current_dir();
2150
        }
2151
#else
2152
        if (!startup_dir)
2153
                startup_dir = g_get_current_dir();
2154
#endif
2155
}
2156

    
2157
void set_rc_dir(const gchar *dir)
2158
{
2159
        if (rc_dir)
2160
                g_free(rc_dir);
2161

    
2162
        if (dir) {
2163
                if (g_path_is_absolute(dir))
2164
                        rc_dir = g_strdup(dir);
2165
                else
2166
                        rc_dir = g_strconcat(get_startup_dir(),
2167
                                             G_DIR_SEPARATOR_S, dir, NULL);
2168
        } else
2169
                rc_dir = NULL;
2170
}
2171

    
2172
const gchar *get_startup_dir(void)
2173
{
2174
        if (!startup_dir)
2175
                set_startup_dir();
2176

    
2177
        return startup_dir;
2178
}
2179

    
2180
#ifdef G_OS_WIN32
2181
static gchar *get_win32_special_folder_path(gint nfolder)
2182
{
2183
        gchar *folder = NULL;
2184
        HRESULT hr;
2185

    
2186
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2187
                wchar_t path[MAX_PATH + 1];
2188
                hr = SHGetFolderPathW(NULL, nfolder, NULL, 0, path);
2189
                if (hr == S_OK)
2190
                        folder = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
2191
        } else {
2192
                gchar path[MAX_PATH + 1];
2193
                hr = SHGetFolderPathA(NULL, nfolder, NULL, 0, path);
2194
                if (hr == S_OK)
2195
                        folder = g_locale_to_utf8(path, -1, NULL, NULL, NULL);
2196
        }
2197

    
2198
        return folder;
2199
}
2200
#endif
2201

    
2202
const gchar *get_home_dir(void)
2203
{
2204
#ifdef G_OS_WIN32
2205
        static const gchar *home_dir = NULL;
2206

    
2207
        if (!home_dir) {
2208
                home_dir = g_get_home_dir();
2209
                if (!home_dir)
2210
                        home_dir = "C:\\Sylpheed";
2211
        }
2212

    
2213
        return home_dir;
2214
#else
2215
        return g_get_home_dir();
2216
#endif
2217
}
2218

    
2219
const gchar *get_document_dir(void)
2220
{
2221
#ifdef G_OS_WIN32
2222
        static const gchar *document_dir = NULL;
2223
        HRESULT hr;
2224

    
2225
        if (!document_dir) {
2226
                document_dir = get_win32_special_folder_path(CSIDL_PERSONAL);
2227
                if (!document_dir)
2228
                        document_dir = get_home_dir();
2229
        }
2230

    
2231
        return document_dir;
2232
#elif defined(__APPLE__)
2233
        static const gchar *document_dir = NULL;
2234

    
2235
        if (!document_dir) {
2236
                document_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2237
                                           "Documents", NULL);
2238
        }
2239

    
2240
        return document_dir;
2241
#else
2242
        return get_home_dir();
2243
#endif
2244
}
2245

    
2246
const gchar *get_rc_dir(void)
2247
{
2248
        if (!rc_dir) {
2249
#ifdef G_OS_WIN32
2250
                gchar *appdata;
2251

    
2252
                appdata = get_win32_special_folder_path(CSIDL_APPDATA);
2253
                if (appdata)
2254
                        rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S,
2255
                                             RC_DIR, NULL);
2256
                else
2257
                        rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2258
                                             RC_DIR, NULL);
2259
                g_free(appdata);
2260
#elif defined(__APPLE__)
2261
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2262
                                     "Library", G_DIR_SEPARATOR_S,
2263
                                     "Application Support", G_DIR_SEPARATOR_S,
2264
                                     RC_DIR, NULL);
2265
#else
2266
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2267
                                     RC_DIR, NULL);
2268
#endif
2269
        }
2270

    
2271
        return rc_dir;
2272
}
2273

    
2274
const gchar *get_old_rc_dir(void)
2275
{
2276
        static gchar *old_rc_dir = NULL;
2277

    
2278
        if (!old_rc_dir)
2279
                old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2280
                                         OLD_RC_DIR, NULL);
2281

    
2282
        return old_rc_dir;
2283
}
2284

    
2285
const gchar *get_mail_base_dir(void)
2286
{
2287
#if defined(G_OS_WIN32) || defined(__APPLE__)
2288
        static gchar *mail_base_dir = NULL;
2289

    
2290
        if (!mail_base_dir)
2291
                mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2292
                                            "Mailboxes", NULL);
2293

    
2294
        return mail_base_dir;
2295
#else
2296
        return get_home_dir();
2297
#endif
2298
}
2299

    
2300
const gchar *get_news_cache_dir(void)
2301
{
2302
        static gchar *news_cache_dir = NULL;
2303

    
2304
        if (!news_cache_dir)
2305
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2306
                                             NEWS_CACHE_DIR, NULL);
2307

    
2308
        return news_cache_dir;
2309
}
2310

    
2311
const gchar *get_imap_cache_dir(void)
2312
{
2313
        static gchar *imap_cache_dir = NULL;
2314

    
2315
        if (!imap_cache_dir)
2316
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2317
                                             IMAP_CACHE_DIR, NULL);
2318

    
2319
        return imap_cache_dir;
2320
}
2321

    
2322
const gchar *get_mime_tmp_dir(void)
2323
{
2324
        static gchar *mime_tmp_dir = NULL;
2325

    
2326
        if (!mime_tmp_dir)
2327
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2328
                                           MIME_TMP_DIR, NULL);
2329

    
2330
        return mime_tmp_dir;
2331
}
2332

    
2333
const gchar *get_template_dir(void)
2334
{
2335
        static gchar *template_dir = NULL;
2336

    
2337
        if (!template_dir)
2338
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2339
                                           TEMPLATE_DIR, NULL);
2340

    
2341
        return template_dir;
2342
}
2343

    
2344
const gchar *get_tmp_dir(void)
2345
{
2346
        static gchar *tmp_dir = NULL;
2347

    
2348
        if (!tmp_dir)
2349
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2350
                                      TMP_DIR, NULL);
2351

    
2352
        return tmp_dir;
2353
}
2354

    
2355
gchar *get_tmp_file(void)
2356
{
2357
        gchar *tmp_file;
2358
        static guint32 id = 0;
2359

    
2360
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2361
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
2362

    
2363
        return tmp_file;
2364
}
2365

    
2366
const gchar *get_domain_name(void)
2367
{
2368
        static gchar *domain_name = NULL;
2369

    
2370
        if (!domain_name) {
2371
                gchar buf[128] = "";
2372
                struct hostent *hp;
2373

    
2374
                if (gethostname(buf, sizeof(buf)) < 0) {
2375
                        perror("gethostname");
2376
                        domain_name = "unknown";
2377
                } else {
2378
                        buf[sizeof(buf) - 1] = '\0';
2379
                        if ((hp = my_gethostbyname(buf)) == NULL) {
2380
                                perror("gethostbyname");
2381
                                domain_name = g_strdup(buf);
2382
                        } else {
2383
                                domain_name = g_strdup(hp->h_name);
2384
                        }
2385
                }
2386

    
2387
                debug_print("domain name = %s\n", domain_name);
2388
                if (is_next_nonascii(domain_name)) {
2389
                        g_warning("invalid domain name: %s\n", domain_name);
2390
                        g_free(domain_name);
2391
                        domain_name = "unknown";
2392
                }
2393
        }
2394

    
2395
        return domain_name;
2396
}
2397

    
2398
off_t get_file_size(const gchar *file)
2399
{
2400
        struct stat s;
2401

    
2402
        if (g_stat(file, &s) < 0) {
2403
                FILE_OP_ERROR(file, "stat");
2404
                return -1;
2405
        }
2406

    
2407
        return s.st_size;
2408
}
2409

    
2410
off_t get_file_size_as_crlf(const gchar *file)
2411
{
2412
        FILE *fp;
2413
        off_t size = 0;
2414
        gchar buf[BUFFSIZE];
2415

    
2416
        if ((fp = g_fopen(file, "rb")) == NULL) {
2417
                FILE_OP_ERROR(file, "fopen");
2418
                return -1;
2419
        }
2420

    
2421
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2422
                strretchomp(buf);
2423
                size += strlen(buf) + 2;
2424
        }
2425

    
2426
        if (ferror(fp)) {
2427
                FILE_OP_ERROR(file, "fgets");
2428
                size = -1;
2429
        }
2430

    
2431
        fclose(fp);
2432

    
2433
        return size;
2434
}
2435

    
2436
off_t get_left_file_size(FILE *fp)
2437
{
2438
        glong pos;
2439
        glong end;
2440
        off_t size;
2441

    
2442
        if ((pos = ftell(fp)) < 0) {
2443
                perror("ftell");
2444
                return -1;
2445
        }
2446
        if (fseek(fp, 0L, SEEK_END) < 0) {
2447
                perror("fseek");
2448
                return -1;
2449
        }
2450
        if ((end = ftell(fp)) < 0) {
2451
                perror("fseek");
2452
                return -1;
2453
        }
2454
        size = end - pos;
2455
        if (fseek(fp, pos, SEEK_SET) < 0) {
2456
                perror("fseek");
2457
                return -1;
2458
        }
2459

    
2460
        return size;
2461
}
2462

    
2463
gint get_last_empty_line_size(FILE *fp, off_t size)
2464
{
2465
        glong pos;
2466
        gint lsize = 0;
2467
        gchar buf[4];
2468
        size_t nread;
2469

    
2470
        if (size < 4)
2471
                return -1;
2472

    
2473
        if ((pos = ftell(fp)) < 0) {
2474
                perror("ftell");
2475
                return -1;
2476
        }
2477
        if (fseek(fp, size - 4, SEEK_CUR) < 0) {
2478
                perror("fseek");
2479
                return -1;
2480
        }
2481

    
2482
        /* read last 4 bytes */
2483
        nread = fread(buf, sizeof(buf), 1, fp);
2484
        if (nread != 1) {
2485
                perror("fread");
2486
                return -1;
2487
        }
2488
        /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */
2489
        if (buf[3] == '\n') {
2490
                if (buf[2] == '\n')
2491
                        lsize = 1;
2492
                else if (buf[2] == '\r') {
2493
                        if (buf[1] == '\n')
2494
                                lsize = 2;
2495
                }
2496
        }
2497

    
2498
        if (fseek(fp, pos, SEEK_SET) < 0) {
2499
                perror("fseek");
2500
                return -1;
2501
        }
2502

    
2503
        return lsize;
2504
}
2505

    
2506
gboolean file_exist(const gchar *file, gboolean allow_fifo)
2507
{
2508
        if (file == NULL)
2509
                return FALSE;
2510

    
2511
        if (allow_fifo) {
2512
                struct stat s;
2513

    
2514
                if (g_stat(file, &s) < 0) {
2515
                        if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2516
                        return FALSE;
2517
                }
2518
                if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode))
2519
                        return TRUE;
2520
        } else {
2521
                return g_file_test(file, G_FILE_TEST_IS_REGULAR);
2522
        }
2523

    
2524
        return FALSE;
2525
}
2526

    
2527
gboolean is_dir_exist(const gchar *dir)
2528
{
2529
        if (dir == NULL)
2530
                return FALSE;
2531

    
2532
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
2533
}
2534

    
2535
gboolean is_file_entry_exist(const gchar *file)
2536
{
2537
        if (file == NULL)
2538
                return FALSE;
2539

    
2540
        return g_file_test(file, G_FILE_TEST_EXISTS);
2541
}
2542

    
2543
gboolean dirent_is_regular_file(struct dirent *d)
2544
{
2545
#ifdef HAVE_DIRENT_D_TYPE
2546
        if (d->d_type == DT_REG)
2547
                return TRUE;
2548
        else if (d->d_type != DT_UNKNOWN)
2549
                return FALSE;
2550
#endif
2551

    
2552
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2553
}
2554

    
2555
gboolean dirent_is_directory(struct dirent *d)
2556
{
2557
#ifdef HAVE_DIRENT_D_TYPE
2558
        if (d->d_type == DT_DIR)
2559
                return TRUE;
2560
        else if (d->d_type != DT_UNKNOWN)
2561
                return FALSE;
2562
#endif
2563

    
2564
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2565
}
2566

    
2567
gint change_dir(const gchar *dir)
2568
{
2569
        gchar *prevdir = NULL;
2570

    
2571
        if (debug_mode)
2572
                prevdir = g_get_current_dir();
2573

    
2574
        if (g_chdir(dir) < 0) {
2575
                FILE_OP_ERROR(dir, "chdir");
2576
                if (debug_mode) g_free(prevdir);
2577
                return -1;
2578
        } else if (debug_mode) {
2579
                gchar *cwd;
2580

    
2581
                cwd = g_get_current_dir();
2582
                if (strcmp(prevdir, cwd) != 0)
2583
                        g_print("current dir: %s\n", cwd);
2584
                g_free(cwd);
2585
                g_free(prevdir);
2586
        }
2587

    
2588
        return 0;
2589
}
2590

    
2591
gint make_dir(const gchar *dir)
2592
{
2593
        if (g_mkdir(dir, S_IRWXU) < 0) {
2594
                FILE_OP_ERROR(dir, "mkdir");
2595
                return -1;
2596
        }
2597
        if (g_chmod(dir, S_IRWXU) < 0)
2598
                FILE_OP_ERROR(dir, "chmod");
2599

    
2600
        return 0;
2601
}
2602

    
2603
gint make_dir_hier(const gchar *dir)
2604
{
2605
        gchar *parent_dir;
2606
        const gchar *p;
2607

    
2608
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2609
                parent_dir = g_strndup(dir, p - dir);
2610
                if (*parent_dir != '\0') {
2611
                        if (!is_dir_exist(parent_dir)) {
2612
                                if (make_dir(parent_dir) < 0) {
2613
                                        g_free(parent_dir);
2614
                                        return -1;
2615
                                }
2616
                        }
2617
                }
2618
                g_free(parent_dir);
2619
        }
2620

    
2621
        if (!is_dir_exist(dir)) {
2622
                if (make_dir(dir) < 0)
2623
                        return -1;
2624
        }
2625

    
2626
        return 0;
2627
}
2628

    
2629
gint remove_all_files(const gchar *dir)
2630
{
2631
        GDir *dp;
2632
        const gchar *dir_name;
2633
        gchar *prev_dir;
2634

    
2635
        prev_dir = g_get_current_dir();
2636

    
2637
        if (g_chdir(dir) < 0) {
2638
                FILE_OP_ERROR(dir, "chdir");
2639
                g_free(prev_dir);
2640
                return -1;
2641
        }
2642

    
2643
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2644
                g_warning("failed to open directory: %s\n", dir);
2645
                g_free(prev_dir);
2646
                return -1;
2647
        }
2648

    
2649
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2650
                if (g_unlink(dir_name) < 0)
2651
                        FILE_OP_ERROR(dir_name, "unlink");
2652
        }
2653

    
2654
        g_dir_close(dp);
2655

    
2656
        if (g_chdir(prev_dir) < 0) {
2657
                FILE_OP_ERROR(prev_dir, "chdir");
2658
                g_free(prev_dir);
2659
                return -1;
2660
        }
2661

    
2662
        g_free(prev_dir);
2663

    
2664
        return 0;
2665
}
2666

    
2667
gint remove_numbered_files(const gchar *dir, guint first, guint last)
2668
{
2669
        GDir *dp;
2670
        const gchar *dir_name;
2671
        gchar *prev_dir;
2672
        guint file_no;
2673

    
2674
        prev_dir = g_get_current_dir();
2675

    
2676
        if (g_chdir(dir) < 0) {
2677
                FILE_OP_ERROR(dir, "chdir");
2678
                g_free(prev_dir);
2679
                return -1;
2680
        }
2681

    
2682
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2683
                g_warning("failed to open directory: %s\n", dir);
2684
                g_free(prev_dir);
2685
                return -1;
2686
        }
2687

    
2688
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2689
                file_no = to_unumber(dir_name);
2690
                if (file_no > 0 && first <= file_no && file_no <= last) {
2691
                        if (is_dir_exist(dir_name))
2692
                                continue;
2693
                        if (g_unlink(dir_name) < 0)
2694
                                FILE_OP_ERROR(dir_name, "unlink");
2695
                }
2696
        }
2697

    
2698
        g_dir_close(dp);
2699

    
2700
        if (g_chdir(prev_dir) < 0) {
2701
                FILE_OP_ERROR(prev_dir, "chdir");
2702
                g_free(prev_dir);
2703
                return -1;
2704
        }
2705

    
2706
        g_free(prev_dir);
2707

    
2708
        return 0;
2709
}
2710

    
2711
gint remove_all_numbered_files(const gchar *dir)
2712
{
2713
        return remove_numbered_files(dir, 0, UINT_MAX);
2714
}
2715

    
2716
gint remove_expired_files(const gchar *dir, guint hours)
2717
{
2718
        GDir *dp;
2719
        const gchar *dir_name;
2720
        struct stat s;
2721
        gchar *prev_dir;
2722
        guint file_no;
2723
        time_t mtime, now, expire_time;
2724

    
2725
        prev_dir = g_get_current_dir();
2726

    
2727
        if (g_chdir(dir) < 0) {
2728
                FILE_OP_ERROR(dir, "chdir");
2729
                g_free(prev_dir);
2730
                return -1;
2731
        }
2732

    
2733
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2734
                g_warning("failed to open directory: %s\n", dir);
2735
                g_free(prev_dir);
2736
                return -1;
2737
        }
2738

    
2739
        now = time(NULL);
2740
        expire_time = hours * 60 * 60;
2741

    
2742
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2743
                file_no = to_unumber(dir_name);
2744
                if (file_no > 0) {
2745
                        if (g_stat(dir_name, &s) < 0) {
2746
                                FILE_OP_ERROR(dir_name, "stat");
2747
                                continue;
2748
                        }
2749
                        if (S_ISDIR(s.st_mode))
2750
                                continue;
2751
                        mtime = MAX(s.st_mtime, s.st_atime);
2752
                        if (now - mtime > expire_time) {
2753
                                if (g_unlink(dir_name) < 0)
2754
                                        FILE_OP_ERROR(dir_name, "unlink");
2755
                        }
2756
                }
2757
        }
2758

    
2759
        g_dir_close(dp);
2760

    
2761
        if (g_chdir(prev_dir) < 0) {
2762
                FILE_OP_ERROR(prev_dir, "chdir");
2763
                g_free(prev_dir);
2764
                return -1;
2765
        }
2766

    
2767
        g_free(prev_dir);
2768

    
2769
        return 0;
2770
}
2771

    
2772
static gint remove_dir_recursive_real(const gchar *dir)
2773
{
2774
        struct stat s;
2775
        GDir *dp;
2776
        const gchar *dir_name;
2777
        gchar *prev_dir;
2778

    
2779
        if (g_stat(dir, &s) < 0) {
2780
                FILE_OP_ERROR(dir, "stat");
2781
                if (ENOENT == errno) return 0;
2782
                return -1;
2783
        }
2784

    
2785
        if (!S_ISDIR(s.st_mode)) {
2786
                if (g_unlink(dir) < 0) {
2787
                        FILE_OP_ERROR(dir, "unlink");
2788
                        return -1;
2789
                }
2790

    
2791
                return 0;
2792
        }
2793

    
2794
        prev_dir = g_get_current_dir();
2795
        /* g_print("prev_dir = %s\n", prev_dir); */
2796

    
2797
        if (g_chdir(dir) < 0) {
2798
                FILE_OP_ERROR(dir, "chdir");
2799
                g_free(prev_dir);
2800
                return -1;
2801
        }
2802

    
2803
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2804
                g_warning("failed to open directory: %s\n", dir);
2805
                g_chdir(prev_dir);
2806
                g_free(prev_dir);
2807
                return -1;
2808
        }
2809

    
2810
        /* remove all files in the directory */
2811
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2812
                /* g_print("removing %s\n", dir_name); */
2813

    
2814
                if (is_dir_exist(dir_name)) {
2815
                        if (remove_dir_recursive_real(dir_name) < 0) {
2816
                                g_warning("can't remove directory\n");
2817
                                return -1;
2818
                        }
2819
                } else {
2820
                        if (g_unlink(dir_name) < 0)
2821
                                FILE_OP_ERROR(dir_name, "unlink");
2822
                }
2823
        }
2824

    
2825
        g_dir_close(dp);
2826

    
2827
        if (g_chdir(prev_dir) < 0) {
2828
                FILE_OP_ERROR(prev_dir, "chdir");
2829
                g_free(prev_dir);
2830
                return -1;
2831
        }
2832

    
2833
        g_free(prev_dir);
2834

    
2835
        if (g_rmdir(dir) < 0) {
2836
                if (ENOTDIR == errno) {
2837
                        if (g_unlink(dir) < 0) {
2838
                                FILE_OP_ERROR(dir, "unlink");
2839
                                return -1;
2840
                        }
2841
                } else {
2842
                        FILE_OP_ERROR(dir, "rmdir");
2843
                        return -1;
2844
                }
2845
        }
2846

    
2847
        return 0;
2848
}
2849

    
2850
gint remove_dir_recursive(const gchar *dir)
2851
{
2852
        gchar *cur_dir;
2853
        gint ret;
2854

    
2855
        debug_print("remove_dir_recursive: %s\n", dir);
2856

    
2857
        cur_dir = g_get_current_dir();
2858

    
2859
        if (g_chdir(dir) < 0) {
2860
                FILE_OP_ERROR(dir, "chdir");
2861
                ret = -1;
2862
                goto leave;
2863
        }
2864
        if (g_chdir("..") < 0) {
2865
                FILE_OP_ERROR(dir, "chdir");
2866
                ret = -1;
2867
                goto leave;
2868
        }
2869

    
2870
        ret = remove_dir_recursive_real(dir);
2871

    
2872
leave:
2873
        if (is_dir_exist(cur_dir)) {
2874
                if (g_chdir(cur_dir) < 0) {
2875
                        FILE_OP_ERROR(cur_dir, "chdir");
2876
                }
2877
        }
2878

    
2879
        g_free(cur_dir);
2880

    
2881
        return ret;
2882
}
2883

    
2884
gint rename_force(const gchar *oldpath, const gchar *newpath)
2885
{
2886
#if !defined(G_OS_UNIX) && !GLIB_CHECK_VERSION(2, 9, 1)
2887
        if (!is_file_entry_exist(oldpath)) {
2888
                errno = ENOENT;
2889
                return -1;
2890
        }
2891
        if (is_file_exist(newpath)) {
2892
                if (g_unlink(newpath) < 0)
2893
                        FILE_OP_ERROR(newpath, "unlink");
2894
        }
2895
#endif
2896
        return g_rename(oldpath, newpath);
2897
}
2898

    
2899
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2900
{
2901
#ifdef G_OS_WIN32
2902
        wchar_t *wsrc;
2903
        wchar_t *wdest;
2904
        gchar *dest_bak = NULL;
2905
        gboolean err = FALSE;
2906

    
2907
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
2908
        if (wsrc == NULL) {
2909
                return -1;
2910
        }
2911
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
2912
        if (wdest == NULL) {
2913
                g_free(wsrc);
2914
                return -1;
2915
        }
2916

    
2917
        if (keep_backup == FALSE) {
2918
                if (CopyFileW(wsrc, wdest, FALSE) == 0)
2919
                        err = TRUE;
2920
                g_free(wdest);
2921
                g_free(wsrc);
2922
                return err ? -1 : 0;
2923
        }
2924

    
2925
        if (is_file_exist(dest)) {
2926
                dest_bak = g_strconcat(dest, ".bak", NULL);
2927
                if (rename_force(dest, dest_bak) < 0) {
2928
                        FILE_OP_ERROR(dest, "rename");
2929
                        g_free(dest_bak);
2930
                        g_free(wdest);
2931
                        g_free(wsrc);
2932
                        return -1;
2933
                }
2934
        }
2935

    
2936
        if (CopyFileW(wsrc, wdest, FALSE) == 0)
2937
                err = TRUE;
2938

    
2939
        g_free(wdest);
2940
        g_free(wsrc);
2941
#else
2942
        gint srcfd, destfd;
2943
        gint n_read;
2944
        gchar buf[BUFFSIZE];
2945
        gchar *dest_bak = NULL;
2946
        gboolean err = FALSE;
2947

    
2948
        if ((srcfd = g_open(src, O_RDONLY, 0600)) < 0) {
2949
                FILE_OP_ERROR(src, "open");
2950
                return -1;
2951
        }
2952
        if (is_file_exist(dest)) {
2953
                dest_bak = g_strconcat(dest, ".bak", NULL);
2954
                if (rename_force(dest, dest_bak) < 0) {
2955
                        FILE_OP_ERROR(dest, "rename");
2956
                        close(srcfd);
2957
                        g_free(dest_bak);
2958
                        return -1;
2959
                }
2960
        }
2961

    
2962
        if ((destfd = g_open(dest, O_WRONLY | O_CREAT, 0600)) < 0) {
2963
                FILE_OP_ERROR(dest, "open");
2964
                close(srcfd);
2965
                if (dest_bak) {
2966
                        if (rename_force(dest_bak, dest) < 0)
2967
                                FILE_OP_ERROR(dest_bak, "rename");
2968
                        g_free(dest_bak);
2969
                }
2970
                return -1;
2971
        }
2972

    
2973
        while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) {
2974
                gchar *p = buf;
2975
                const gchar *endp = buf + n_read;
2976
                gint n_write;
2977

    
2978
                while (p < endp) {
2979
                        if ((n_write = write(destfd, p, endp - p)) < 0) {
2980
                                g_warning(_("writing to %s failed.\n"), dest);
2981
                                close(destfd);
2982
                                close(srcfd);
2983
                                g_unlink(dest);
2984
                                if (dest_bak) {
2985
                                        if (rename_force(dest_bak, dest) < 0)
2986
                                                FILE_OP_ERROR(dest_bak, "rename");
2987
                                        g_free(dest_bak);
2988
                                }
2989
                                return -1;
2990
                        }
2991
                        p += n_write;
2992
                }
2993
        }
2994

    
2995
        if (close(destfd) < 0) {
2996
                FILE_OP_ERROR(dest, "close");
2997
                err = TRUE;
2998
        }
2999
        close(srcfd);
3000
#endif
3001

    
3002
        if (err) {
3003
                g_unlink(dest);
3004
                if (dest_bak) {
3005
                        if (rename_force(dest_bak, dest) < 0)
3006
                                FILE_OP_ERROR(dest_bak, "rename");
3007
                        g_free(dest_bak);
3008
                }
3009
                return -1;
3010
        }
3011

    
3012
        if (keep_backup == FALSE && dest_bak)
3013
                g_unlink(dest_bak);
3014

    
3015
        g_free(dest_bak);
3016

    
3017
        return 0;
3018
}
3019

    
3020
gint copy_dir(const gchar *src, const gchar *dest)
3021
{
3022
        GDir *dir;
3023
        const gchar *dir_name;
3024
        gchar *src_file;
3025
        gchar *dest_file;
3026

    
3027
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
3028
                g_warning("failed to open directory: %s\n", src);
3029
                return -1;
3030
        }
3031

    
3032
        if (make_dir_hier(dest) < 0) {
3033
                g_dir_close(dir);
3034
                return -1;
3035
        }
3036

    
3037
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
3038
                src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
3039
                dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
3040
                                        NULL);
3041
                if (is_file_exist(src_file))
3042
                        copy_file(src_file, dest_file, FALSE);
3043
                g_free(dest_file);
3044
                g_free(src_file);
3045
        }
3046

    
3047
        g_dir_close(dir);
3048

    
3049
        return 0;
3050
}
3051

    
3052
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
3053
{
3054
        if (overwrite == FALSE && is_file_entry_exist(dest)) {
3055
                g_warning("move_file(): file %s already exists.", dest);
3056
                return -1;
3057
        }
3058

    
3059
        if (rename_force(src, dest) == 0) return 0;
3060

    
3061
        if (EXDEV != errno) {
3062
                FILE_OP_ERROR(src, "rename");
3063
                return -1;
3064
        }
3065

    
3066
        if (copy_file(src, dest, FALSE) < 0) return -1;
3067

    
3068
        g_unlink(src);
3069

    
3070
        return 0;
3071
}
3072

    
3073
gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
3074
{
3075
        gint n_read;
3076
        gint bytes_left, to_read;
3077
        gchar buf[BUFSIZ];
3078

    
3079
        g_return_val_if_fail(fp != NULL, -1);
3080
        g_return_val_if_fail(dest_fp != NULL, -1);
3081

    
3082
        if (fseek(fp, offset, SEEK_SET) < 0) {
3083
                perror("fseek");
3084
                return -1;
3085
        }
3086

    
3087
        bytes_left = length;
3088
        to_read = MIN(bytes_left, sizeof(buf));
3089

    
3090
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
3091
                if (n_read < to_read && ferror(fp))
3092
                        break;
3093
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3094
                        g_warning("append_file_part: writing to file failed.\n");
3095
                        return -1;
3096
                }
3097
                bytes_left -= n_read;
3098
                if (bytes_left == 0)
3099
                        break;
3100
                to_read = MIN(bytes_left, sizeof(buf));
3101
        }
3102

    
3103
        if (ferror(fp)) {
3104
                perror("fread");
3105
                return -1;
3106
        }
3107
        if (fflush(dest_fp) == EOF) {
3108
                FILE_OP_ERROR("append_file_part", "fflush");
3109
                return -1;
3110
        }
3111

    
3112
        return 0;
3113
}
3114

    
3115
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
3116
{
3117
        FILE *dest_fp;
3118

    
3119
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3120
                FILE_OP_ERROR(dest, "fopen");
3121
                return -1;
3122
        }
3123

    
3124
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3125
                FILE_OP_ERROR(dest, "chmod");
3126
                g_warning("can't change file mode\n");
3127
        }
3128

    
3129
        if (append_file_part(fp, offset, length, dest_fp) < 0) {
3130
                g_warning("writing to %s failed.\n", dest);
3131
                fclose(dest_fp);
3132
                g_unlink(dest);
3133
                return -1;
3134
        }
3135

    
3136
        if (fclose(dest_fp) == EOF) {
3137
                FILE_OP_ERROR(dest, "fclose");
3138
                g_unlink(dest);
3139
                return -1;
3140
        }
3141

    
3142
        return 0;
3143
}
3144

    
3145
gint copy_file_stream(FILE *fp, FILE *dest_fp)
3146
{
3147
        gint n_read;
3148
        gchar buf[BUFFSIZE];
3149

    
3150
        g_return_val_if_fail(fp != NULL, -1);
3151
        g_return_val_if_fail(dest_fp != NULL, -1);
3152

    
3153
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3154
                if (n_read < sizeof(buf) && ferror(fp))
3155
                        break;
3156
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3157
                        g_warning("copy_file_stream: writing to file failed.\n");
3158
                        return -1;
3159
                }
3160
        }
3161

    
3162
        if (ferror(fp)) {
3163
                perror("fread");
3164
                return -1;
3165
        }
3166
        if (fflush(dest_fp) == EOF) {
3167
                FILE_OP_ERROR("copy_file_stream", "fflush");
3168
                return -1;
3169
        }
3170

    
3171
        return 0;
3172
}
3173

    
3174
/* convert line endings into CRLF. If the last line doesn't end with
3175
 * linebreak, add it.
3176
 */
3177
gchar *canonicalize_str(const gchar *str)
3178
{
3179
        const gchar *p;
3180
        guint new_len = 0;
3181
        gchar *out, *outp;
3182

    
3183
        for (p = str; *p != '\0'; ++p) {
3184
                if (*p != '\r') {
3185
                        ++new_len;
3186
                        if (*p == '\n')
3187
                                ++new_len;
3188
                }
3189
        }
3190
        if (p == str || *(p - 1) != '\n')
3191
                new_len += 2;
3192

    
3193
        out = outp = g_malloc(new_len + 1);
3194
        for (p = str; *p != '\0'; ++p) {
3195
                if (*p != '\r') {
3196
                        if (*p == '\n')
3197
                                *outp++ = '\r';
3198
                        *outp++ = *p;
3199
                }
3200
        }
3201
        if (p == str || *(p - 1) != '\n') {
3202
                *outp++ = '\r';
3203
                *outp++ = '\n';
3204
        }
3205
        *outp = '\0';
3206

    
3207
        return out;
3208
}
3209

    
3210
gint canonicalize_file(const gchar *src, const gchar *dest)
3211
{
3212
        FILE *src_fp, *dest_fp;
3213
        gchar buf[BUFFSIZE];
3214
        gint len;
3215
        gboolean err = FALSE;
3216
        gboolean last_linebreak = FALSE;
3217

    
3218
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3219
                FILE_OP_ERROR(src, "fopen");
3220
                return -1;
3221
        }
3222

    
3223
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3224
                FILE_OP_ERROR(dest, "fopen");
3225
                fclose(src_fp);
3226
                return -1;
3227
        }
3228

    
3229
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3230
                FILE_OP_ERROR(dest, "chmod");
3231
                g_warning("can't change file mode\n");
3232
        }
3233

    
3234
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3235
                gint r = 0;
3236

    
3237
                len = strlen(buf);
3238
                if (len == 0) break;
3239
                last_linebreak = FALSE;
3240

    
3241
                if (buf[len - 1] != '\n') {
3242
                        last_linebreak = TRUE;
3243
                        r = fputs(buf, dest_fp);
3244
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3245
                        r = fputs(buf, dest_fp);
3246
                } else {
3247
                        if (len > 1) {
3248
                                r = fwrite(buf, len - 1, 1, dest_fp);
3249
                                if (r != 1)
3250
                                        r = EOF;
3251
                        }
3252
                        if (r != EOF)
3253
                                r = fputs("\r\n", dest_fp);
3254
                }
3255

    
3256
                if (r == EOF) {
3257
                        g_warning("writing to %s failed.\n", dest);
3258
                        fclose(dest_fp);
3259
                        fclose(src_fp);
3260
                        g_unlink(dest);
3261
                        return -1;
3262
                }
3263
        }
3264

    
3265
        if (last_linebreak == TRUE) {
3266
                if (fputs("\r\n", dest_fp) == EOF)
3267
                        err = TRUE;
3268
        }
3269

    
3270
        if (ferror(src_fp)) {
3271
                FILE_OP_ERROR(src, "fgets");
3272
                err = TRUE;
3273
        }
3274
        fclose(src_fp);
3275
        if (fclose(dest_fp) == EOF) {
3276
                FILE_OP_ERROR(dest, "fclose");
3277
                err = TRUE;
3278
        }
3279

    
3280
        if (err) {
3281
                g_unlink(dest);
3282
                return -1;
3283
        }
3284

    
3285
        return 0;
3286
}
3287

    
3288
gint canonicalize_file_replace(const gchar *file)
3289
{
3290
        gchar *tmp_file;
3291

    
3292
        tmp_file = get_tmp_file();
3293

    
3294
        if (canonicalize_file(file, tmp_file) < 0) {
3295
                g_free(tmp_file);
3296
                return -1;
3297
        }
3298

    
3299
        if (move_file(tmp_file, file, TRUE) < 0) {
3300
                g_warning("can't replace %s .\n", file);
3301
                g_unlink(tmp_file);
3302
                g_free(tmp_file);
3303
                return -1;
3304
        }
3305

    
3306
        g_free(tmp_file);
3307
        return 0;
3308
}
3309

    
3310
FILE *canonicalize_file_stream(FILE *src_fp, gint *length)
3311
{
3312
        FILE *dest_fp;
3313
        gchar buf[BUFFSIZE];
3314
        gint len;
3315
        gint length_ = 0;
3316
        gboolean err = FALSE;
3317
        gboolean last_linebreak = FALSE;
3318

    
3319
        if ((dest_fp = my_tmpfile()) == NULL)
3320
                return NULL;
3321

    
3322
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3323
                gint r = 0;
3324

    
3325
                len = strlen(buf);
3326
                if (len == 0) break;
3327
                last_linebreak = FALSE;
3328

    
3329
                if (buf[len - 1] != '\n') {
3330
                        last_linebreak = TRUE;
3331
                        r = fputs(buf, dest_fp);
3332
                        length_ += len;
3333
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3334
                        r = fputs(buf, dest_fp);
3335
                        length_ += len;
3336
                } else {
3337
                        if (len > 1) {
3338
                                r = fwrite(buf, len - 1, 1, dest_fp);
3339
                                if (r != 1)
3340
                                        r = EOF;
3341
                                else
3342
                                        length_ += len - 1;
3343
                        }
3344
                        if (r != EOF) {
3345
                                r = fputs("\r\n", dest_fp);
3346
                                length_ += 2;
3347
                        }
3348
                }
3349

    
3350
                if (r == EOF) {
3351
                        g_warning("writing to temporary file failed.\n");
3352
                        fclose(dest_fp);
3353
                        return NULL;
3354
                }
3355
        }
3356

    
3357
        if (last_linebreak == TRUE) {
3358
                if (fputs("\r\n", dest_fp) == EOF)
3359
                        err = TRUE;
3360
                else
3361
                        length_ += 2;
3362
        }
3363

    
3364
        if (ferror(src_fp)) {
3365
                FILE_OP_ERROR("canonicalize_file_stream", "fgets");
3366
                err = TRUE;
3367
        }
3368
        if (fflush(dest_fp) == EOF) {
3369
                FILE_OP_ERROR("canonicalize_file_stream", "fflush");
3370
                err = TRUE;
3371
        }
3372

    
3373
        if (err) {
3374
                fclose(dest_fp);
3375
                return NULL;
3376
        }
3377

    
3378
        if (length)
3379
                *length = length_;
3380

    
3381
        rewind(dest_fp);
3382
        return dest_fp;
3383
}
3384

    
3385
gint uncanonicalize_file(const gchar *src, const gchar *dest)
3386
{
3387
        FILE *src_fp, *dest_fp;
3388
        gchar buf[BUFFSIZE];
3389
        gboolean err = FALSE;
3390

    
3391
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3392
                FILE_OP_ERROR(src, "fopen");
3393
                return -1;
3394
        }
3395

    
3396
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3397
                FILE_OP_ERROR(dest, "fopen");
3398
                fclose(src_fp);
3399
                return -1;
3400
        }
3401

    
3402
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3403
                FILE_OP_ERROR(dest, "chmod");
3404
                g_warning("can't change file mode\n");
3405
        }
3406

    
3407
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3408
                strcrchomp(buf);
3409
                if (fputs(buf, dest_fp) == EOF) {
3410
                        g_warning("writing to %s failed.\n", dest);
3411
                        fclose(dest_fp);
3412
                        fclose(src_fp);
3413
                        g_unlink(dest);
3414
                        return -1;
3415
                }
3416
        }
3417

    
3418
        if (ferror(src_fp)) {
3419
                FILE_OP_ERROR(src, "fgets");
3420
                err = TRUE;
3421
        }
3422
        fclose(src_fp);
3423
        if (fclose(dest_fp) == EOF) {
3424
                FILE_OP_ERROR(dest, "fclose");
3425
                err = TRUE;
3426
        }
3427

    
3428
        if (err) {
3429
                g_unlink(dest);
3430
                return -1;
3431
        }
3432

    
3433
        return 0;
3434
}
3435

    
3436
gint uncanonicalize_file_replace(const gchar *file)
3437
{
3438
        gchar *tmp_file;
3439

    
3440
        tmp_file = get_tmp_file();
3441

    
3442
        if (uncanonicalize_file(file, tmp_file) < 0) {
3443
                g_free(tmp_file);
3444
                return -1;
3445
        }
3446

    
3447
        if (move_file(tmp_file, file, TRUE) < 0) {
3448
                g_warning("can't replace %s .\n", file);
3449
                g_unlink(tmp_file);
3450
                g_free(tmp_file);
3451
                return -1;
3452
        }
3453

    
3454
        g_free(tmp_file);
3455
        return 0;
3456
}
3457

    
3458
gchar *normalize_newlines(const gchar *str)
3459
{
3460
        const gchar *p = str;
3461
        gchar *out, *outp;
3462

    
3463
        out = outp = g_malloc(strlen(str) + 1);
3464
        for (p = str; *p != '\0'; ++p) {
3465
                if (*p == '\r') {
3466
                        if (*(p + 1) != '\n')
3467
                                *outp++ = '\n';
3468
                } else
3469
                        *outp++ = *p;
3470
        }
3471

    
3472
        *outp = '\0';
3473

    
3474
        return out;
3475
}
3476

    
3477
gchar *strchomp_all(const gchar *str)
3478
{
3479
        const gchar *p = str;
3480
        const gchar *newline, *last;
3481
        gchar *out, *outp;
3482

    
3483
        out = outp = g_malloc(strlen(str) + 1);
3484
        while (*p != '\0') {
3485
                newline = strchr(p, '\n');
3486
                if (newline) {
3487
                        for (last = newline;
3488
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3489
                                ;
3490
                        strncpy(outp, p, last - p);
3491
                        outp += last - p;
3492

    
3493
                        if (p < newline && *(newline - 1) == '\r') {
3494
                                strncpy(outp, newline - 1, 2);
3495
                                outp += 2;
3496
                        } else {
3497
                                *outp++ = *newline;
3498
                        }
3499

    
3500
                        p = newline + 1;
3501
                } else {
3502
                        for (last = p + strlen(p);
3503
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3504
                                ;
3505
                        strncpy(outp, p, last - p);
3506
                        outp += last - p;
3507
                        break;
3508
                }
3509
        }
3510

    
3511
        *outp = '\0';
3512

    
3513
        return out;
3514
}
3515

    
3516
FILE *get_outgoing_rfc2822_file(FILE *fp)
3517
{
3518
        gchar buf[BUFFSIZE];
3519
        FILE *outfp;
3520

    
3521
        outfp = my_tmpfile();
3522
        if (!outfp) {
3523
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile");
3524
                return NULL;
3525
        }
3526

    
3527
        /* output header part */
3528
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3529
                strretchomp(buf);
3530
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3531
                        gint next;
3532

    
3533
                        for (;;) {
3534
                                next = fgetc(fp);
3535
                                if (next == EOF)
3536
                                        break;
3537
                                else if (next != ' ' && next != '\t') {
3538
                                        ungetc(next, fp);
3539
                                        break;
3540
                                }
3541
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3542
                                        break;
3543
                        }
3544
                } else {
3545
                        if (fputs(buf, outfp) == EOF)
3546
                                goto file_error;
3547
                        if (fputs("\r\n", outfp) == EOF)
3548
                                goto file_error;
3549
                        if (buf[0] == '\0')
3550
                                break;
3551
                }
3552
        }
3553

    
3554
        /* output body part */
3555
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3556
                strretchomp(buf);
3557
                if (buf[0] == '.') {
3558
                        if (fputc('.', outfp) == EOF)
3559
                                goto file_error;
3560
                }
3561
                if (fputs(buf, outfp) == EOF)
3562
                        goto file_error;
3563
                if (fputs("\r\n", outfp) == EOF)
3564
                        goto file_error;
3565
        }
3566

    
3567
        if (fflush(outfp) == EOF) {
3568
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush");
3569
                goto file_error;
3570
        }
3571

    
3572
        rewind(outfp);
3573
        return outfp;
3574

    
3575
file_error:
3576
        g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n");
3577
        fclose(outfp);
3578
        return NULL;
3579
}
3580

    
3581
gchar *get_outgoing_rfc2822_str(FILE *fp)
3582
{
3583
        gchar buf[BUFFSIZE];
3584
        GString *str;
3585
        gchar *ret;
3586

    
3587
        str = g_string_new(NULL);
3588

    
3589
        /* output header part */
3590
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3591
                strretchomp(buf);
3592
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3593
                        gint next;
3594

    
3595
                        for (;;) {
3596
                                next = fgetc(fp);
3597
                                if (next == EOF)
3598
                                        break;
3599
                                else if (next != ' ' && next != '\t') {
3600
                                        ungetc(next, fp);
3601
                                        break;
3602
                                }
3603
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3604
                                        break;
3605
                        }
3606
#if 0
3607
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
3608
                        get_rfc822_date(buf, sizeof(buf));
3609
                        g_string_append_printf(str, "Date: %s\r\n", buf);
3610
#endif
3611
                } else {
3612
                        g_string_append(str, buf);
3613
                        g_string_append(str, "\r\n");
3614
                        if (buf[0] == '\0')
3615
                                break;
3616
                }
3617
        }
3618

    
3619
        /* output body part */
3620
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3621
                strretchomp(buf);
3622
                if (buf[0] == '.')
3623
                        g_string_append_c(str, '.');
3624
                g_string_append(str, buf);
3625
                g_string_append(str, "\r\n");
3626
        }
3627

    
3628
        ret = str->str;
3629
        g_string_free(str, FALSE);
3630

    
3631
        return ret;
3632
}
3633

    
3634
/*
3635
 * Create a new boundary in a way that it is very unlikely that this
3636
 * will occur in the following text.  It would be easy to ensure
3637
 * uniqueness if everything is either quoted-printable or base64
3638
 * encoded (note that conversion is allowed), but because MIME bodies
3639
 * may be nested, it may happen that the same boundary has already
3640
 * been used. We avoid scanning the message for conflicts and hope the
3641
 * best.
3642
 *
3643
 *   boundary := 0*69<bchars> bcharsnospace
3644
 *   bchars := bcharsnospace / " "
3645
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3646
 *                    "+" / "_" / "," / "-" / "." /
3647
 *                    "/" / ":" / "=" / "?"
3648
 *
3649
 * some special characters removed because of buggy MTAs
3650
 */
3651

    
3652
gchar *generate_mime_boundary(const gchar *prefix)
3653
{
3654
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3655
                             "abcdefghijklmnopqrstuvwxyz"
3656
                             "1234567890+_./=";
3657
        gchar buf_uniq[17];
3658
        gchar buf_date[64];
3659
        gint i;
3660

    
3661
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3662
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3663
        buf_uniq[i] = '\0';
3664

    
3665
        get_rfc822_date(buf_date, sizeof(buf_date));
3666
        subst_chars(buf_date, " ,:", '_');
3667

    
3668
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
3669
                               buf_date, buf_uniq);
3670
}
3671

    
3672
gint change_file_mode_rw(FILE *fp, const gchar *file)
3673
{
3674
#ifdef G_OS_WIN32
3675
        DWORD attr;
3676
        BOOL retval;
3677

    
3678
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3679
                wchar_t *wpath;
3680

    
3681
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3682
                if (wpath == NULL)
3683
                        return -1;
3684

    
3685
                attr = GetFileAttributesW(wpath);
3686
                retval = SetFileAttributesW
3687
                        (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3688

    
3689
                g_free(wpath);
3690
        } else {
3691
                gchar *cp_path;
3692

    
3693
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3694
                if (cp_path == NULL)
3695
                        return -1;
3696

    
3697
                attr = GetFileAttributesA(cp_path);
3698
                retval = SetFileAttributesA
3699
                        (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3700

    
3701
                g_free(cp_path);
3702
        }
3703

    
3704
        if (retval)
3705
                return 0;
3706
        else
3707
                return -1;
3708
#else
3709
#if HAVE_FCHMOD
3710
        if (fp)
3711
                return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3712
        else
3713
#endif
3714
                return g_chmod(file, S_IRUSR|S_IWUSR);
3715
#endif
3716
}
3717

    
3718
#ifdef G_OS_WIN32
3719
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
3720
{
3721
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3722
                wchar_t *wpath;
3723
                wchar_t *wprefix;
3724
                wchar_t *wname;
3725
                gint save_errno;
3726
                gchar *name;
3727

    
3728
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
3729
                if (wpath == NULL) {
3730
                        errno = EINVAL;
3731
                        return NULL;
3732
                }
3733
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
3734
                if (wprefix == NULL) {
3735
                        errno = EINVAL;
3736
                        g_free(wpath);
3737
                        return NULL;
3738
                }
3739

    
3740
                wname = _wtempnam(wpath, wprefix);
3741
                save_errno = errno;
3742

    
3743
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
3744
                if (name == NULL) {
3745
                        save_errno = EINVAL;
3746
                }
3747

    
3748
                g_free(wname);
3749
                g_free(wprefix);
3750
                g_free(wpath);
3751

    
3752
                errno = save_errno;
3753
                return name;
3754
        } else {
3755
                gchar *cp_path;
3756
                gchar *cp_prefix;
3757
                gchar *cp_name;
3758
                gint save_errno;
3759
                gchar *name;
3760

    
3761
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
3762
                if (cp_path == NULL) {
3763
                        errno = EINVAL;
3764
                        return NULL;
3765
                }
3766

    
3767
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
3768
                if (cp_prefix == NULL) {
3769
                        errno = EINVAL;
3770
                        g_free(cp_path);
3771
                        return NULL;
3772
                }
3773

    
3774
                cp_name = _tempnam(cp_path, cp_prefix);
3775
                save_errno = errno;
3776

    
3777
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
3778
                if (name == NULL) {
3779
                        save_errno = EINVAL;
3780
                }
3781

    
3782
                g_free(cp_name);
3783
                g_free(cp_prefix);
3784
                g_free(cp_path);
3785

    
3786
                errno = save_errno;
3787
                return name;
3788
        }
3789
}
3790
#endif
3791

    
3792
FILE *my_tmpfile(void)
3793
{
3794
#ifdef G_OS_WIN32
3795
        const gchar *tmpdir;
3796
        gchar *fname;
3797
        gint fd;
3798
        FILE *fp;
3799

    
3800
        tmpdir = get_tmp_dir();
3801
        fname = _s_tempnam(tmpdir, "sylph");
3802
        if (!fname)
3803
                return NULL;
3804

    
3805
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
3806
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
3807
        if (fd < 0) {
3808
                g_free(fname);
3809
                return NULL;
3810
        }
3811

    
3812
        fp = fdopen(fd, "w+b");
3813
        if (!fp) {
3814
                perror("fdopen");
3815
                close(fd);
3816
        }
3817

    
3818
        g_free(fname);
3819

    
3820
        return fp;
3821
#else
3822
        const gchar suffix[] = ".XXXXXX";
3823
        const gchar *tmpdir;
3824
        guint tmplen;
3825
        const gchar *progname;
3826
        guint proglen;
3827
        gchar *fname;
3828
        gint fd;
3829
        FILE *fp;
3830

    
3831
        tmpdir = get_tmp_dir();
3832
        tmplen = strlen(tmpdir);
3833
        progname = g_get_prgname();
3834
        if (!progname)
3835
                progname = "sylph";
3836
        proglen = strlen(progname);
3837
        fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix));
3838

    
3839
        memcpy(fname, tmpdir, tmplen);
3840
        fname[tmplen] = G_DIR_SEPARATOR;
3841
        memcpy(fname + tmplen + 1, progname, proglen);
3842
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3843

    
3844
        fd = g_mkstemp(fname);
3845
        if (fd < 0) {
3846
                g_free(fname);
3847
                return tmpfile();
3848
        }
3849

    
3850
        g_unlink(fname);
3851

    
3852
        fp = fdopen(fd, "w+b");
3853
        if (!fp) {
3854
                perror("fdopen");
3855
                close(fd);
3856
        }
3857

    
3858
        g_free(fname);
3859

    
3860
        return fp;
3861
#endif
3862
}
3863

    
3864
FILE *str_open_as_stream(const gchar *str)
3865
{
3866
        FILE *fp;
3867
        size_t len;
3868

    
3869
        g_return_val_if_fail(str != NULL, NULL);
3870

    
3871
        fp = my_tmpfile();
3872
        if (!fp) {
3873
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3874
                return NULL;
3875
        }
3876

    
3877
        len = strlen(str);
3878
        if (len == 0) return fp;
3879

    
3880
        if (fwrite(str, len, 1, fp) != 1) {
3881
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3882
                fclose(fp);
3883
                return NULL;
3884
        }
3885
        if (fflush(fp) == EOF) {
3886
                FILE_OP_ERROR("str_open_as_stream", "fflush");
3887
                fclose(fp);
3888
                return NULL;
3889
        }
3890

    
3891
        rewind(fp);
3892
        return fp;
3893
}
3894

    
3895
gint str_write_to_file(const gchar *str, const gchar *file)
3896
{
3897
        FILE *fp;
3898
        size_t len;
3899

    
3900
        g_return_val_if_fail(str != NULL, -1);
3901
        g_return_val_if_fail(file != NULL, -1);
3902

    
3903
        if ((fp = g_fopen(file, "wb")) == NULL) {
3904
                FILE_OP_ERROR(file, "fopen");
3905
                return -1;
3906
        }
3907

    
3908
        len = strlen(str);
3909
        if (len == 0) {
3910
                fclose(fp);
3911
                return 0;
3912
        }
3913

    
3914
        if (fwrite(str, len, 1, fp) != 1) {
3915
                FILE_OP_ERROR(file, "fwrite");
3916
                fclose(fp);
3917
                g_unlink(file);
3918
                return -1;
3919
        }
3920

    
3921
        if (fclose(fp) == EOF) {
3922
                FILE_OP_ERROR(file, "fclose");
3923
                g_unlink(file);
3924
                return -1;
3925
        }
3926

    
3927
        return 0;
3928
}
3929

    
3930
gchar *file_read_to_str(const gchar *file)
3931
{
3932
        FILE *fp;
3933
        gchar *str;
3934

    
3935
        g_return_val_if_fail(file != NULL, NULL);
3936

    
3937
        if ((fp = g_fopen(file, "rb")) == NULL) {
3938
                FILE_OP_ERROR(file, "fopen");
3939
                return NULL;
3940
        }
3941

    
3942
        str = file_read_stream_to_str(fp);
3943

    
3944
        fclose(fp);
3945

    
3946
        return str;
3947
}
3948

    
3949
gchar *file_read_stream_to_str(FILE *fp)
3950
{
3951
        GByteArray *array;
3952
        guchar buf[BUFSIZ];
3953
        gint n_read;
3954
        gchar *str;
3955

    
3956
        g_return_val_if_fail(fp != NULL, NULL);
3957

    
3958
        array = g_byte_array_new();
3959

    
3960
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3961
                if (n_read < sizeof(buf) && ferror(fp))
3962
                        break;
3963
                g_byte_array_append(array, buf, n_read);
3964
        }
3965

    
3966
        if (ferror(fp)) {
3967
                FILE_OP_ERROR("file stream", "fread");
3968
                g_byte_array_free(array, TRUE);
3969
                return NULL;
3970
        }
3971

    
3972
        buf[0] = '\0';
3973
        g_byte_array_append(array, buf, 1);
3974
        str = (gchar *)array->data;
3975
        g_byte_array_free(array, FALSE);
3976

    
3977
        return str;
3978
}
3979

    
3980
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3981
static gchar **argv_utf8_to_locale(gchar **argv)
3982
{
3983
        gint argc = 0, i;
3984
        gchar **cp_argv;
3985

    
3986
        while (argv[argc] != NULL)
3987
                argc++;
3988

    
3989
        cp_argv = g_new(gchar *, argc + 1);
3990

    
3991
        for (i = 0; i < argc; i++) {
3992
                cp_argv[i] = g_locale_from_utf8(argv[i], -1, NULL, NULL, NULL);
3993
                if (cp_argv[i] == NULL) {
3994
                        g_warning("Failed to convert from UTF-8 to locale encoding: %s\n", argv[i]);
3995
                        g_strfreev(cp_argv);
3996
                        return NULL;
3997
                }
3998
        }
3999
        cp_argv[i] = NULL;
4000

    
4001
        return cp_argv;
4002
}
4003
#endif
4004

    
4005
gint execute_async(gchar *const argv[])
4006
{
4007
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
4008
        gchar **cp_argv;
4009

    
4010
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
4011

    
4012
        cp_argv = argv_utf8_to_locale((gchar **)argv);
4013
        if (!cp_argv)
4014
                return -1;
4015
        if (g_spawn_async(NULL, cp_argv, NULL, G_SPAWN_SEARCH_PATH,
4016
                          NULL, NULL, NULL, NULL) == FALSE) {
4017
                g_warning("Can't execute command: %s\n", argv[0]);
4018
                g_strfreev(cp_argv);
4019
                return -1;
4020
        }
4021
        g_strfreev(cp_argv);
4022
#else
4023
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
4024

    
4025
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
4026
                          NULL, NULL, NULL, NULL) == FALSE) {
4027
                g_warning("Can't execute command: %s\n", argv[0]);
4028
                return -1;
4029
        }
4030
#endif
4031

    
4032
        return 0;
4033
}
4034

    
4035
gint execute_sync(gchar *const argv[])
4036
{
4037
        gint status;
4038
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
4039
        gchar **cp_argv;
4040
#endif
4041

    
4042
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
4043

    
4044
#ifdef G_OS_WIN32
4045
#if !GLIB_CHECK_VERSION(2, 8, 2)
4046
        cp_argv = argv_utf8_to_locale((gchar **)argv);
4047
        if (!cp_argv)
4048
                return -1;
4049
        if (g_spawn_sync(NULL, cp_argv, NULL,
4050
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
4051
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
4052
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4053
                g_warning("Can't execute command: %s\n", argv[0]);
4054
                g_strfreev(cp_argv);
4055
                return -1;
4056
        }
4057
        g_strfreev(cp_argv);
4058
#else /* !GLIB_CHECK_VERSION */
4059
        if (g_spawn_sync(NULL, (gchar **)argv, NULL,
4060
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
4061
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
4062
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4063
                g_warning("Can't execute command: %s\n", argv[0]);
4064
                return -1;
4065
        }
4066
#endif /* !GLIB_CHECK_VERSION */
4067

    
4068
        return status;
4069
#else /* G_OS_WIN32 */
4070
        if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
4071
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4072
                g_warning("Can't execute command: %s\n", argv[0]);
4073
                return -1;
4074
        }
4075

    
4076
        if (WIFEXITED(status))
4077
                return WEXITSTATUS(status);
4078
        else
4079
                return -1;
4080
#endif /* G_OS_WIN32 */
4081
}
4082

    
4083
gint execute_command_line(const gchar *cmdline, gboolean async)
4084
{
4085
        gchar **argv;
4086
        gint ret;
4087

    
4088
        if (debug_mode) {
4089
                gchar *utf8_cmdline;
4090

    
4091
                utf8_cmdline = g_filename_to_utf8
4092
                        (cmdline, -1, NULL, NULL, NULL);
4093
                debug_print("execute_command_line(): executing: %s\n",
4094
                            utf8_cmdline ? utf8_cmdline : cmdline);
4095
                g_free(utf8_cmdline);
4096
        }
4097

    
4098
        argv = strsplit_with_quote(cmdline, " ", 0);
4099

    
4100
        if (async)
4101
                ret = execute_async(argv);
4102
        else
4103
                ret = execute_sync(argv);
4104

    
4105
        g_strfreev(argv);
4106

    
4107
        return ret;
4108
}
4109

    
4110
#if USE_THREADS
4111
typedef struct _CmdData
4112
{
4113
        const gchar *cmdline;
4114
        volatile gint flag;
4115
        gint status;
4116
} CmdData;
4117

    
4118
static gpointer execute_command_line_async_func(gpointer data)
4119
{
4120
        CmdData *cmd_data = (CmdData *)data;
4121
        gchar **argv;
4122

    
4123
        argv = strsplit_with_quote(cmd_data->cmdline, " ", 0);
4124
        cmd_data->status = execute_sync(argv);
4125
        g_strfreev(argv);
4126

    
4127
        debug_print("execute_command_line_async_func: exec done: %s\n",
4128
                    cmd_data->cmdline);
4129
        g_atomic_int_set(&cmd_data->flag, 1);
4130
        g_main_context_wakeup(NULL);
4131

    
4132
        return GINT_TO_POINTER(0);
4133
}
4134

    
4135
gint execute_command_line_async_wait(const gchar *cmdline)
4136
{
4137
        CmdData data = {NULL, 0, 0};
4138
        GThread *thread;
4139

    
4140
        if (debug_mode) {
4141
                gchar *utf8_cmdline;
4142

    
4143
                utf8_cmdline = g_filename_to_utf8
4144
                        (cmdline, -1, NULL, NULL, NULL);
4145
                debug_print("execute_command_line(): executing: %s\n",
4146
                            utf8_cmdline ? utf8_cmdline : cmdline);
4147
                g_free(utf8_cmdline);
4148
        }
4149

    
4150
        data.cmdline = cmdline;
4151
        thread = g_thread_create(execute_command_line_async_func, &data, TRUE,
4152
                                 NULL);
4153
        if (!thread)
4154
                return -1;
4155

    
4156
        debug_print("execute_command_line_async_wait: waiting thread\n");
4157
        while (g_atomic_int_get(&data.flag) == 0)
4158
                event_loop_iterate();
4159

    
4160
        g_thread_join(thread);
4161
        debug_print("execute_command_line_async_wait: thread exited\n");
4162

    
4163
        return data.status;
4164
}
4165
#else /* USE_THREADS */
4166
gint execute_command_line_async_wait(const gchar *cmdline)
4167
{
4168
        return execute_command_line(cmdline, FALSE);
4169
}
4170
#endif /* USE_THREADS */
4171

    
4172
gint execute_open_file(const gchar *file, const gchar *content_type)
4173
{
4174
#ifdef G_OS_WIN32
4175
        g_return_val_if_fail(file != NULL, -1);
4176

    
4177
        log_print("opening %s - %s\n", file, content_type ? content_type : "");
4178

    
4179
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4180
                wchar_t *wpath;
4181

    
4182
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4183
                if (wpath == NULL)
4184
                        return -1;
4185

    
4186
                ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL);
4187

    
4188
                g_free(wpath);
4189

    
4190
                return 0;
4191
        } else {
4192
                gchar *cp_path;
4193

    
4194
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4195
                if (cp_path == NULL)
4196
                        return -1;
4197

    
4198
                ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL);
4199

    
4200
                g_free(cp_path);
4201

    
4202
                return 0;
4203
        }
4204
#elif defined(__APPLE__)
4205
        const gchar *argv[3] = {"open", NULL, NULL};
4206

    
4207
        g_return_val_if_fail(file != NULL, -1);
4208

    
4209
        log_print("opening %s - %s\n", file, content_type ? content_type : "");
4210

    
4211
        argv[1] = file;
4212
        execute_async(argv);
4213
#endif
4214

    
4215
        return 0;
4216
}
4217

    
4218
gint execute_print_file(const gchar *file)
4219
{
4220
        g_return_val_if_fail(file != NULL, -1);
4221

    
4222
#ifdef G_OS_WIN32
4223
        log_print("printing %s\n", file);
4224

    
4225
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4226
                wchar_t *wpath;
4227

    
4228
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4229
                if (wpath == NULL)
4230
                        return -1;
4231

    
4232
                ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL);
4233

    
4234
                g_free(wpath);
4235

    
4236
                return 0;
4237
        } else {
4238
                gchar *cp_path;
4239

    
4240
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4241
                if (cp_path == NULL)
4242
                        return -1;
4243

    
4244
                ShellExecuteA(NULL, "print", cp_path, NULL, NULL,
4245
                              SW_SHOWNORMAL);
4246

    
4247
                g_free(cp_path);
4248

    
4249
                return 0;
4250
        }
4251
#endif
4252
        return 0;
4253
}
4254

    
4255
gchar *get_command_output(const gchar *cmdline)
4256
{
4257
        gchar *child_stdout;
4258
        gint status;
4259

    
4260
        g_return_val_if_fail(cmdline != NULL, NULL);
4261

    
4262
        debug_print("get_command_output(): executing: %s\n", cmdline);
4263

    
4264
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
4265
                                      NULL) == FALSE) {
4266
                g_warning("Can't execute command: %s\n", cmdline);
4267
                return NULL;
4268
        }
4269

    
4270
        return child_stdout;
4271
}
4272

    
4273
gint open_uri(const gchar *uri, const gchar *cmdline)
4274
{
4275
        gchar buf[BUFFSIZE];
4276

    
4277
        g_return_val_if_fail(uri != NULL, -1);
4278

    
4279
#if defined(G_OS_WIN32) || defined(__APPLE__)
4280
        if (!cmdline || cmdline[0] == '\0')
4281
                return execute_open_file(uri, NULL);
4282
#endif
4283

    
4284
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
4285
                g_snprintf(buf, sizeof(buf), cmdline, uri);
4286
        else {
4287
                if (cmdline)
4288
                        g_warning("Open URI command line is invalid "
4289
                                  "(there must be only one '%%s'): %s",
4290
                                  cmdline);
4291
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
4292
        }
4293

    
4294
        execute_command_line(buf, TRUE);
4295

    
4296
        return 0;
4297
}
4298

    
4299
gint play_sound(const gchar *file, gboolean async)
4300
{
4301
#ifdef G_OS_WIN32
4302
        wchar_t *wfile;
4303
        DWORD flag = SND_FILENAME;
4304

    
4305
        wfile = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4306
        if (wfile == NULL)
4307
                return -1;
4308
        if (async)
4309
                flag |= SND_ASYNC;
4310
        else
4311
                flag |= SND_SYNC;
4312
        PlaySoundW(wfile, NULL, flag);
4313
        g_free(wfile);
4314
#endif
4315
        return 0;
4316
}
4317

    
4318
time_t remote_tzoffset_sec(const gchar *zone)
4319
{
4320
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
4321
        gchar zone3[4];
4322
        gchar *p;
4323
        gchar c;
4324
        gint iustz;
4325
        gint offset;
4326
        time_t remoteoffset;
4327

    
4328
        strncpy(zone3, zone, 3);
4329
        zone3[3] = '\0';
4330
        remoteoffset = 0;
4331

    
4332
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
4333
            (c == '+' || c == '-')) {
4334
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
4335
                if (c == '-')
4336
                        remoteoffset = -remoteoffset;
4337
        } else if (!strncmp(zone, "UT" , 2) ||
4338
                   !strncmp(zone, "GMT", 2)) {
4339
                remoteoffset = 0;
4340
        } else if (strlen(zone3) == 3) {
4341
                for (p = ustzstr; *p != '\0'; p += 3) {
4342
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
4343
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
4344
                                remoteoffset = iustz * 3600;
4345
                                break;
4346
                        }
4347
                }
4348
                if (*p == '\0')
4349
                        return -1;
4350
        } else if (strlen(zone3) == 1) {
4351
                switch (zone[0]) {
4352
                case 'Z': remoteoffset =   0; break;
4353
                case 'A': remoteoffset =  -1; break;
4354
                case 'B': remoteoffset =  -2; break;
4355
                case 'C': remoteoffset =  -3; break;
4356
                case 'D': remoteoffset =  -4; break;
4357
                case 'E': remoteoffset =  -5; break;
4358
                case 'F': remoteoffset =  -6; break;
4359
                case 'G': remoteoffset =  -7; break;
4360
                case 'H': remoteoffset =  -8; break;
4361
                case 'I': remoteoffset =  -9; break;
4362
                case 'K': remoteoffset = -10; break; /* J is not used */
4363
                case 'L': remoteoffset = -11; break;
4364
                case 'M': remoteoffset = -12; break;
4365
                case 'N': remoteoffset =   1; break;
4366
                case 'O': remoteoffset =   2; break;
4367
                case 'P': remoteoffset =   3; break;
4368
                case 'Q': remoteoffset =   4; break;
4369
                case 'R': remoteoffset =   5; break;
4370
                case 'S': remoteoffset =   6; break;
4371
                case 'T': remoteoffset =   7; break;
4372
                case 'U': remoteoffset =   8; break;
4373
                case 'V': remoteoffset =   9; break;
4374
                case 'W': remoteoffset =  10; break;
4375
                case 'X': remoteoffset =  11; break;
4376
                case 'Y': remoteoffset =  12; break;
4377
                default:  remoteoffset =   0; break;
4378
                }
4379
                remoteoffset = remoteoffset * 3600;
4380
        } else
4381
                return -1;
4382

    
4383
        return remoteoffset;
4384
}
4385

    
4386
time_t tzoffset_sec(time_t *now)
4387
{
4388
        struct tm gmt, *tmp, *lt;
4389
        gint off;
4390

    
4391
        tmp = gmtime(now);
4392
        g_return_val_if_fail(tmp != NULL, -1);
4393
        gmt = *tmp;
4394
        lt = localtime(now);
4395
        g_return_val_if_fail(lt != NULL, -1);
4396

    
4397
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4398

    
4399
        if (lt->tm_year < gmt.tm_year)
4400
                off -= 24 * 60;
4401
        else if (lt->tm_year > gmt.tm_year)
4402
                off += 24 * 60;
4403
        else if (lt->tm_yday < gmt.tm_yday)
4404
                off -= 24 * 60;
4405
        else if (lt->tm_yday > gmt.tm_yday)
4406
                off += 24 * 60;
4407

    
4408
        if (off >= 24 * 60)                /* should be impossible */
4409
                off = 23 * 60 + 59;        /* if not, insert silly value */
4410
        if (off <= -24 * 60)
4411
                off = -(23 * 60 + 59);
4412

    
4413
        return off * 60;
4414
}
4415

    
4416
/* calculate timezone offset (buf must not be less than 6 bytes) */
4417
gchar *tzoffset_buf(gchar *buf, time_t *now)
4418
{
4419
        struct tm gmt, *tmp, *lt;
4420
        gint off;
4421
        gchar sign = '+';
4422

    
4423
        tmp = gmtime(now);
4424
        g_return_val_if_fail(tmp != NULL, NULL);
4425
        gmt = *tmp;
4426
        lt = localtime(now);
4427
        g_return_val_if_fail(lt != NULL, NULL);
4428

    
4429
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4430

    
4431
        if (lt->tm_year < gmt.tm_year)
4432
                off -= 24 * 60;
4433
        else if (lt->tm_year > gmt.tm_year)
4434
                off += 24 * 60;
4435
        else if (lt->tm_yday < gmt.tm_yday)
4436
                off -= 24 * 60;
4437
        else if (lt->tm_yday > gmt.tm_yday)
4438
                off += 24 * 60;
4439

    
4440
        if (off < 0) {
4441
                sign = '-';
4442
                off = -off;
4443
        }
4444

    
4445
        if (off >= 24 * 60)                /* should be impossible */
4446
                off = 23 * 60 + 59;        /* if not, insert silly value */
4447

    
4448
        g_snprintf(buf, 6, "%c%02d%02d", sign, off / 60, off % 60);
4449

    
4450
        return buf;
4451
}
4452

    
4453
gchar *tzoffset(time_t *now)
4454
{
4455
        static gchar offset_string[6];
4456

    
4457
        return tzoffset_buf(offset_string, now);
4458
}
4459

    
4460
void get_rfc822_date(gchar *buf, gint len)
4461
{
4462
        struct tm *lt;
4463
        time_t t;
4464
        gchar day[4], mon[4];
4465
        gint dd, hh, mm, ss, yyyy;
4466
        gchar off[6];
4467

    
4468
        t = time(NULL);
4469
        lt = localtime(&t);
4470

    
4471
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
4472
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
4473
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
4474
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t));
4475
}
4476

    
4477
/* just a wrapper to suppress the warning of gcc about %c */
4478
size_t my_strftime(gchar *s, size_t max, const gchar *format,
4479
                   const struct tm *tm)
4480
{
4481
        return strftime(s, max, format, tm);
4482
}
4483

    
4484
/* UI hints */
4485

    
4486
static UIUpdateFunc ui_update_func = NULL;
4487

    
4488
void set_ui_update_func(UIUpdateFunc func)
4489
{
4490
        ui_update_func = func;
4491
}
4492

    
4493
void ui_update(void)
4494
{
4495
        if (ui_update_func)
4496
                ui_update_func();
4497
}
4498

    
4499
static EventLoopFunc event_loop_func = NULL;
4500

    
4501
void set_event_loop_func(EventLoopFunc func)
4502
{
4503
        event_loop_func = func;
4504
}
4505

    
4506
void event_loop_iterate(void)
4507
{
4508
        if (event_loop_func)
4509
                event_loop_func();
4510
        else
4511
                g_main_context_iteration(NULL, TRUE);
4512
}
4513

    
4514
static ProgressFunc progress_func = NULL;
4515

    
4516
void set_progress_func(ProgressFunc func)
4517
{
4518
        progress_func = func;
4519
}
4520

    
4521
void progress_show(gint cur, gint total)
4522
{
4523
        if (progress_func)
4524
                progress_func(cur, total);
4525
}
4526

    
4527
/* user input */
4528

    
4529
static QueryPasswordFunc query_password_func = NULL;
4530

    
4531
void set_input_query_password_func(QueryPasswordFunc func)
4532
{
4533
        query_password_func = func;
4534
}
4535

    
4536
gchar *input_query_password(const gchar *server, const gchar *user)
4537
{
4538
        if (query_password_func)
4539
                return query_password_func(server, user);
4540
        else
4541
                return NULL;
4542
}
4543

    
4544
/* logging */
4545

    
4546
static FILE *log_fp = NULL;
4547
#if USE_THREADS
4548
G_LOCK_DEFINE_STATIC(log_fp);
4549
#define S_LOCK(name)        G_LOCK(name)
4550
#define S_UNLOCK(name)        G_UNLOCK(name)
4551
#else
4552
#define S_LOCK(name)
4553
#define S_UNLOCK(name)
4554
#endif
4555

    
4556
void set_log_file(const gchar *filename)
4557
{
4558
        S_LOCK(log_fp);
4559
        if (!log_fp) {
4560
                log_fp = g_fopen(filename, "w");
4561
                if (!log_fp)
4562
                        FILE_OP_ERROR(filename, "fopen");
4563
        }
4564
        S_UNLOCK(log_fp);
4565
}
4566

    
4567
void close_log_file(void)
4568
{
4569
        S_LOCK(log_fp);
4570
        if (log_fp) {
4571
                fclose(log_fp);
4572
                log_fp = NULL;
4573
        }
4574
        S_UNLOCK(log_fp);
4575
}
4576

    
4577
static guint log_verbosity_count = 0;
4578

    
4579
void set_log_verbosity(gboolean verbose)
4580
{
4581
        if (verbose)
4582
                log_verbosity_count++;
4583
        else if (log_verbosity_count > 0)
4584
                log_verbosity_count--;
4585
}
4586

    
4587
gboolean get_debug_mode(void)
4588
{
4589
        return debug_mode;
4590
}
4591

    
4592
void set_debug_mode(gboolean enable)
4593
{
4594
        debug_mode = enable;
4595
}
4596

    
4597
static void log_dummy_func(const gchar *str)
4598
{
4599
}
4600

    
4601
static void log_dummy_flush_func(void)
4602
{
4603
}
4604

    
4605
static LogFunc log_print_ui_func = log_dummy_func;
4606
static LogFunc log_message_ui_func = log_dummy_func;
4607
static LogFunc log_warning_ui_func = log_dummy_func;
4608
static LogFunc log_error_ui_func = log_dummy_func;
4609
static LogFlushFunc log_flush_ui_func = log_dummy_flush_func;
4610

    
4611
static LogFunc log_show_status_func = log_dummy_func;
4612

    
4613
void set_log_ui_func(LogFunc print_func, LogFunc message_func,
4614
                     LogFunc warning_func, LogFunc error_func)
4615
{
4616
        log_print_ui_func = print_func;
4617
        log_message_ui_func = message_func;
4618
        log_warning_ui_func = warning_func;
4619
        log_error_ui_func = error_func;
4620
}
4621

    
4622
void set_log_ui_func_full(LogFunc print_func, LogFunc message_func,
4623
                          LogFunc warning_func, LogFunc error_func,
4624
                          LogFlushFunc flush_func)
4625
{
4626
        set_log_ui_func(print_func, message_func, warning_func, error_func);
4627
        log_flush_ui_func = flush_func;
4628
}
4629

    
4630
void set_log_show_status_func(LogFunc status_func)
4631
{
4632
        log_show_status_func = status_func;
4633
}
4634

    
4635
void debug_print(const gchar *format, ...)
4636
{
4637
        va_list args;
4638
        gchar buf[BUFFSIZE];
4639

    
4640
        if (!debug_mode) return;
4641

    
4642
        va_start(args, format);
4643
        g_vsnprintf(buf, sizeof(buf), format, args);
4644
        va_end(args);
4645

    
4646
        g_print("%s", buf);
4647
}
4648

    
4649
void status_print(const gchar *format, ...)
4650
{
4651
        va_list args;
4652
        gchar buf[BUFFSIZE];
4653

    
4654
        va_start(args, format);
4655
        g_vsnprintf(buf, sizeof(buf), format, args);
4656
        va_end(args);
4657

    
4658
        log_show_status_func(buf);
4659
}
4660

    
4661
#define TIME_LEN        11
4662

    
4663
void log_write(const gchar *str, const gchar *prefix)
4664
{
4665
        S_LOCK(log_fp);
4666

    
4667
        if (log_fp) {
4668
                gchar buf[TIME_LEN + 1];
4669
                time_t t;
4670

    
4671
                time(&t);
4672
                strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4673

    
4674
                fputs(buf, log_fp);
4675
                if (prefix)
4676
                        fputs(prefix, log_fp);
4677
                fputs(str, log_fp);
4678
                fflush(log_fp);
4679
        }
4680

    
4681
        S_UNLOCK(log_fp);
4682
}
4683

    
4684
void log_print(const gchar *format, ...)
4685
{
4686
        va_list args;
4687
        gchar buf[BUFFSIZE + TIME_LEN];
4688
        time_t t;
4689

    
4690
        time(&t);
4691
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4692

    
4693
        va_start(args, format);
4694
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4695
        va_end(args);
4696

    
4697
        if (debug_mode) g_print("%s", buf);
4698
        log_print_ui_func(buf);
4699
        S_LOCK(log_fp);
4700
        if (log_fp) {
4701
                fputs(buf, log_fp);
4702
                fflush(log_fp);
4703
        }
4704
        S_UNLOCK(log_fp);
4705
        if (log_verbosity_count)
4706
                log_show_status_func(buf + TIME_LEN);
4707
}
4708

    
4709
void log_message(const gchar *format, ...)
4710
{
4711
        va_list args;
4712
        gchar buf[BUFFSIZE + TIME_LEN];
4713
        time_t t;
4714

    
4715
        time(&t);
4716
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4717

    
4718
        va_start(args, format);
4719
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4720
        va_end(args);
4721

    
4722
        if (debug_mode) g_message("%s", buf + TIME_LEN);
4723
        log_message_ui_func(buf + TIME_LEN);
4724
        S_LOCK(log_fp);
4725
        if (log_fp) {
4726
                fwrite(buf, TIME_LEN, 1, log_fp);
4727
                fputs("* message: ", log_fp);
4728
                fputs(buf + TIME_LEN, log_fp);
4729
                fflush(log_fp);
4730
        }
4731
        S_UNLOCK(log_fp);
4732
        log_show_status_func(buf + TIME_LEN);
4733
}
4734

    
4735
void log_warning(const gchar *format, ...)
4736
{
4737
        va_list args;
4738
        gchar buf[BUFFSIZE + TIME_LEN];
4739
        time_t t;
4740

    
4741
        time(&t);
4742
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4743

    
4744
        va_start(args, format);
4745
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4746
        va_end(args);
4747

    
4748
        g_warning("%s", buf);
4749
        log_warning_ui_func(buf + TIME_LEN);
4750
        S_LOCK(log_fp);
4751
        if (log_fp) {
4752
                fwrite(buf, TIME_LEN, 1, log_fp);
4753
                fputs("** warning: ", log_fp);
4754
                fputs(buf + TIME_LEN, log_fp);
4755
                fflush(log_fp);
4756
        }
4757
        S_UNLOCK(log_fp);
4758
}
4759

    
4760
void log_error(const gchar *format, ...)
4761
{
4762
        va_list args;
4763
        gchar buf[BUFFSIZE + TIME_LEN];
4764
        time_t t;
4765

    
4766
        time(&t);
4767
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4768

    
4769
        va_start(args, format);
4770
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4771
        va_end(args);
4772

    
4773
        g_warning("%s", buf);
4774
        log_error_ui_func(buf + TIME_LEN);
4775
        S_LOCK(log_fp);
4776
        if (log_fp) {
4777
                fwrite(buf, TIME_LEN, 1, log_fp);
4778
                fputs("*** error: ", log_fp);
4779
                fputs(buf + TIME_LEN, log_fp);
4780
                fflush(log_fp);
4781
        }
4782
        S_UNLOCK(log_fp);
4783
}
4784

    
4785
void log_flush(void)
4786
{
4787
        S_LOCK(log_fp);
4788
        if (log_fp)
4789
                fflush(log_fp);
4790
        S_UNLOCK(log_fp);
4791
        log_flush_ui_func();
4792
}