Statistics
| Revision:

root / libsylph / utils.c @ 3245

History | View | Annotate | Download (90 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
#include "utils.h"
56
#include "socket.h"
57

    
58
#define BUFFSIZE        8192
59

    
60
static gboolean debug_mode = FALSE;
61

    
62

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

    
72
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
73
                if (wpath == NULL) {
74
                        errno = EINVAL;
75
                        return -1;
76
                }
77

    
78
                retval = _wchdir(wpath);
79
                save_errno = errno;
80

    
81
                g_free(wpath);
82

    
83
                errno = save_errno;
84
                return retval;
85
        } else {
86
                gchar *cp_path;
87
                gint retval;
88
                gint save_errno;
89

    
90
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
91
                if (cp_path == NULL) {
92
                        errno = EINVAL;
93
                        return -1;
94
                }
95

    
96
                retval = chdir(cp_path);
97
                save_errno = errno;
98

    
99
                g_free(cp_path);
100

    
101
                errno = save_errno;
102
                return retval;
103
        }
104
#else
105
        return chdir(path);
106
#endif
107
}
108

    
109
gint g_chmod(const gchar *path, gint mode)
110
{
111
#ifdef G_OS_WIN32
112
        if (G_WIN32_HAVE_WIDECHAR_API()) {
113
                wchar_t *wpath;
114
                gint retval;
115
                gint save_errno;
116

    
117
                wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
118
                if (wpath == NULL) {
119
                        errno = EINVAL;
120
                        return -1;
121
                }
122

    
123
                retval = _wchmod(wpath, mode);
124
                save_errno = errno;
125

    
126
                g_free(wpath);
127

    
128
                errno = save_errno;
129
                return retval;
130
        } else {
131
                gchar *cp_path;
132
                gint retval;
133
                gint save_errno;
134

    
135
                cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
136
                if (cp_path == NULL) {
137
                        errno = EINVAL;
138
                        return -1;
139
                }
140

    
141
                retval = chmod(cp_path, mode);
142
                save_errno = errno;
143

    
144
                g_free(cp_path);
145

    
146
                errno = save_errno;
147
                return retval;
148
        }
149
#else
150
        return chmod(path, mode);
151
#endif
152
}
153
#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
154

    
155
#ifndef G_OS_UNIX
156
gint syl_link(const gchar *src, const gchar *dest)
157
{
158
#ifdef G_OS_WIN32
159
        wchar_t *wsrc;
160
        wchar_t *wdest;
161
        gint retval;
162
        gint save_errno;
163

    
164
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
165
        if (wsrc == NULL) {
166
                errno = EINVAL;
167
                return -1;
168
        }
169
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
170
        if (wdest == NULL) {
171
                g_free(wsrc);
172
                errno = EINVAL;
173
                return -1;
174
        }
175

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

    
203
        g_free(wdest);
204
        g_free(wsrc);
205

    
206
        errno = save_errno;
207
        return retval;
208
#else
209
        return link(src, dest);
210
#endif
211
}
212
#endif /* !G_OS_UNIX */
213

    
214
void list_free_strings(GList *list)
215
{
216
        list = g_list_first(list);
217

    
218
        while (list != NULL) {
219
                g_free(list->data);
220
                list = list->next;
221
        }
222
}
223

    
224
void slist_free_strings(GSList *list)
225
{
226
        while (list != NULL) {
227
                g_free(list->data);
228
                list = list->next;
229
        }
230
}
231

    
232
static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
233
{
234
        g_free(key);
235
}
236

    
237
void hash_free_strings(GHashTable *table)
238
{
239
        g_hash_table_foreach(table, hash_free_strings_func, NULL);
240
}
241

    
242
static void hash_free_value_mem_func(gpointer key, gpointer value,
243
                                     gpointer data)
244
{
245
        g_free(value);
246
}
247

    
248
void hash_free_value_mem(GHashTable *table)
249
{
250
        g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
251
}
252

    
253
gint str_case_equal(gconstpointer v, gconstpointer v2)
254
{
255
        return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
256
}
257

    
258
guint str_case_hash(gconstpointer key)
259
{
260
        const gchar *p = key;
261
        guint h = *p;
262

    
263
        if (h) {
264
                h = g_ascii_tolower(h);
265
                for (p += 1; *p != '\0'; p++)
266
                        h = (h << 5) - h + g_ascii_tolower(*p);
267
        }
268

    
269
        return h;
270
}
271

    
272
void ptr_array_free_strings(GPtrArray *array)
273
{
274
        gint i;
275
        gchar *str;
276

    
277
        g_return_if_fail(array != NULL);
278

    
279
        for (i = 0; i < array->len; i++) {
280
                str = g_ptr_array_index(array, i);
281
                g_free(str);
282
        }
283
}
284

    
285
gboolean str_find(const gchar *haystack, const gchar *needle)
286
{
287
        return strstr(haystack, needle) != NULL ? TRUE : FALSE;
288
}
289

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

    
295
gboolean str_find_equal(const gchar *haystack, const gchar *needle)
296
{
297
        return strcmp(haystack, needle) == 0;
298
}
299

    
300
gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
301
{
302
        return g_ascii_strcasecmp(haystack, needle) == 0;
303
}
304

    
305
gint to_number(const gchar *nstr)
306
{
307
        register const gchar *p;
308

    
309
        if (*nstr == '\0') return -1;
310

    
311
        for (p = nstr; *p != '\0'; p++)
312
                if (!g_ascii_isdigit(*p)) return -1;
313

    
314
        return atoi(nstr);
315
}
316

    
317
guint to_unumber(const gchar *nstr)
318
{
319
        register const gchar *p;
320
        gulong val;
321

    
322
        if (*nstr == '\0') return 0;
323

    
324
        for (p = nstr; *p != '\0'; p++)
325
                if (!g_ascii_isdigit(*p)) return 0;
326

    
327
        errno = 0;
328
        val = strtoul(nstr, NULL, 10);
329
        if (val == ULONG_MAX && errno != 0)
330
                val = 0;
331

    
332
        return (guint)val;
333
}
334

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

    
343
/* convert integer into string */
344
gchar *itos(gint n)
345
{
346
        static gchar nstr[11];
347

    
348
        return itos_buf(nstr, n);
349
}
350

    
351
gchar *utos_buf(gchar *nstr, guint n)
352
{
353
        g_snprintf(nstr, 11, "%u", n);
354
        return nstr;
355
}
356

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

    
368
        return buf;
369
}
370

    
371
gchar *to_human_readable(gint64 size)
372
{
373
        static gchar str[16];
374

    
375
        return to_human_readable_buf(str, sizeof(str), size);
376
}
377

    
378
/* strcmp with NULL-checking */
379
gint strcmp2(const gchar *s1, const gchar *s2)
380
{
381
        if (s1 == NULL || s2 == NULL)
382
                return -1;
383
        else
384
                return strcmp(s1, s2);
385
}
386

    
387
/* compare paths */
388
gint path_cmp(const gchar *s1, const gchar *s2)
389
{
390
        gint len1, len2;
391
#ifdef G_OS_WIN32
392
        gchar *s1_, *s2_;
393
#endif
394

    
395
        if (s1 == NULL || s2 == NULL) return -1;
396
        if (*s1 == '\0' || *s2 == '\0') return -1;
397

    
398
        len1 = strlen(s1);
399
        len2 = strlen(s2);
400

    
401
#ifdef G_OS_WIN32
402
        Xstrdup_a(s1_, s1, return -1);
403
        Xstrdup_a(s2_, s2, return -1);
404
        subst_char(s1_, '/', G_DIR_SEPARATOR);
405
        subst_char(s2_, '/', G_DIR_SEPARATOR);
406
        if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--;
407
        if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--;
408

    
409
        return strncmp(s1_, s2_, MAX(len1, len2));
410
#else
411
        if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
412
        if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
413

    
414
        return strncmp(s1, s2, MAX(len1, len2));
415
#endif
416
}
417

    
418
/* return TRUE if parent is equal to or ancestor of child */
419
gboolean is_path_parent(const gchar *parent, const gchar *child)
420
{
421
        gint plen;
422
        const gchar *base;
423

    
424
        g_return_val_if_fail(parent != NULL, FALSE);
425
        g_return_val_if_fail(child != NULL, FALSE);
426

    
427
        plen = strlen(parent);
428
        while (plen > 0 && G_IS_DIR_SEPARATOR(parent[plen - 1]))
429
                plen--;
430

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

    
442
        return FALSE;
443
}
444

    
445
/* remove trailing return code */
446
gchar *strretchomp(gchar *str)
447
{
448
        register gchar *s;
449

    
450
        if (!*str) return str;
451

    
452
        for (s = str + strlen(str) - 1;
453
             s >= str && (*s == '\n' || *s == '\r');
454
             s--)
455
                *s = '\0';
456

    
457
        return str;
458
}
459

    
460
/* remove trailing character */
461
gchar *strtailchomp(gchar *str, gchar tail_char)
462
{
463
        register gchar *s;
464

    
465
        if (!*str) return str;
466
        if (tail_char == '\0') return str;
467

    
468
        for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
469
                *s = '\0';
470

    
471
        return str;
472
}
473

    
474
/* remove CR (carriage return) */
475
gchar *strcrchomp(gchar *str)
476
{
477
        register gchar *s;
478

    
479
        if (!*str) return str;
480

    
481
        s = str + strlen(str) - 1;
482
        if (*s == '\n' && s > str && *(s - 1) == '\r') {
483
                *(s - 1) = '\n';
484
                *s = '\0';
485
        }
486

    
487
        return str;
488
}
489

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

    
495
        haystack_len = strlen(haystack);
496
        needle_len   = strlen(needle);
497

    
498
        if (haystack_len < needle_len || needle_len == 0)
499
                return NULL;
500

    
501
        while (haystack_len >= needle_len) {
502
                if (!g_ascii_strncasecmp(haystack, needle, needle_len))
503
                        return (gchar *)haystack;
504
                else {
505
                        haystack++;
506
                        haystack_len--;
507
                }
508
        }
509

    
510
        return NULL;
511
}
512

    
513
gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
514
                   gconstpointer needle, size_t needlelen)
515
{
516
        const gchar *haystack_ = (const gchar *)haystack;
517
        const gchar *needle_ = (const gchar *)needle;
518
        const gchar *haystack_cur = (const gchar *)haystack;
519
        size_t haystack_left = haystacklen;
520

    
521
        if (needlelen == 1)
522
                return memchr(haystack_, *needle_, haystacklen);
523

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

    
536
        return NULL;
537
}
538

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

    
545
        while (--n && *s)
546
                *d++ = *s++;
547
        *d = '\0';
548

    
549
        return dest;
550
}
551

    
552
/* Similar to g_str_has_suffix() but case-insensitive */
553
gboolean str_has_suffix_case(const gchar *str, const gchar *suffix)
554
{
555
        size_t len, s_len;
556

    
557
        if (!str || !suffix)
558
                return FALSE;
559

    
560
        len = strlen(str);
561
        s_len = strlen(suffix);
562

    
563
        if (s_len > len)
564
                return FALSE;
565

    
566
        return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0);
567
}
568

    
569
gint str_find_format_times(const gchar *haystack, gchar ch)
570
{
571
        gint n = 0;
572
        const gchar *p = haystack;
573

    
574
        while ((p = strchr(p, '%')) != NULL) {
575
                ++p;
576
                if (*p == '%') {
577
                        ++p;
578
                } else if (*p == ch) {
579
                        ++p;
580
                        ++n;
581
                } else
582
                        return -1;
583
        }
584

    
585
        return n;
586
}
587

    
588
/* Examine if next block is non-ASCII string */
589
gboolean is_next_nonascii(const gchar *s)
590
{
591
        const gchar *p;
592
        gboolean in_quote = FALSE;
593

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

    
607
        return FALSE;
608
}
609

    
610
gint get_next_word_len(const gchar *s)
611
{
612
        const gchar *p = s;
613
        gboolean in_quote = FALSE;
614

    
615
        while (*p != '\0') {
616
                if (!in_quote && g_ascii_isspace(*p))
617
                        break;
618
                if (*p == '"')
619
                        in_quote ^= TRUE;
620
                ++p;
621
        }
622

    
623
        return p - s;
624
}
625

    
626
/* compare subjects */
627
gint subject_compare(const gchar *s1, const gchar *s2)
628
{
629
        gchar *str1, *str2;
630

    
631
        if (!s1 || !s2) return -1;
632
        if (!*s1 || !*s2) return -1;
633

    
634
        Xstrdup_a(str1, s1, return -1);
635
        Xstrdup_a(str2, s2, return -1);
636

    
637
        trim_subject_for_compare(str1);
638
        trim_subject_for_compare(str2);
639

    
640
        if (!*str1 || !*str2) return -1;
641

    
642
        return strcmp(str1, str2);
643
}
644

    
645
gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
646
{
647
        gchar *str1, *str2;
648

    
649
        if (!s1 || !s2) return -1;
650

    
651
        Xstrdup_a(str1, s1, return -1);
652
        Xstrdup_a(str2, s2, return -1);
653

    
654
        trim_subject_for_sort(str1);
655
        trim_subject_for_sort(str2);
656

    
657
        return g_ascii_strcasecmp(str1, str2);
658
}
659

    
660
void trim_subject_for_compare(gchar *str)
661
{
662
        gchar *srcp;
663

    
664
        eliminate_parenthesis(str, '[', ']');
665
        eliminate_parenthesis(str, '(', ')');
666
        g_strstrip(str);
667

    
668
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
669
                srcp = str + 3;
670
                while (g_ascii_isspace(*srcp)) srcp++;
671
                memmove(str, srcp, strlen(srcp) + 1);
672
        }
673
}
674

    
675
void trim_subject_for_sort(gchar *str)
676
{
677
        gchar *srcp;
678

    
679
        g_strstrip(str);
680

    
681
        while (!g_ascii_strncasecmp(str, "Re:", 3)) {
682
                srcp = str + 3;
683
                while (g_ascii_isspace(*srcp)) srcp++;
684
                memmove(str, srcp, strlen(srcp) + 1);
685
        }
686
}
687

    
688
void trim_subject(gchar *str)
689
{
690
        register gchar *srcp, *destp;
691
        gchar op, cl;
692
        gint in_brace;
693

    
694
        destp = str;
695
        while (!g_ascii_strncasecmp(destp, "Re:", 3)) {
696
                destp += 3;
697
                while (g_ascii_isspace(*destp)) destp++;
698
        }
699

    
700
        if (*destp == '[') {
701
                op = '[';
702
                cl = ']';
703
        } else if (*destp == '(') {
704
                op = '(';
705
                cl = ')';
706
        } else
707
                return;
708

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

    
724
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
725
{
726
        register gchar *srcp, *destp;
727
        gint in_brace;
728

    
729
        srcp = destp = str;
730

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

    
748
void extract_parenthesis(gchar *str, gchar op, gchar cl)
749
{
750
        register gchar *srcp, *destp;
751
        gint in_brace;
752

    
753
        srcp = destp = str;
754

    
755
        while ((srcp = strchr(destp, op))) {
756
                if (destp > str)
757
                        *destp++ = ' ';
758
                memmove(destp, srcp + 1, strlen(srcp));
759
                in_brace = 1;
760
                while(*destp) {
761
                        if (*destp == op)
762
                                in_brace++;
763
                        else if (*destp == cl)
764
                                in_brace--;
765

    
766
                        if (in_brace == 0)
767
                                break;
768

    
769
                        destp++;
770
                }
771
        }
772
        *destp = '\0';
773
}
774

    
775
void extract_parenthesis_with_escape(gchar *str, gchar op, gchar cl)
776
{
777
        register gchar *srcp, *destp;
778
        gint in_brace;
779

    
780
        srcp = destp = str;
781

    
782
        while ((srcp = strchr(srcp, op))) {
783
                if (destp > str)
784
                        *destp++ = ' ';
785
                ++srcp;
786
                in_brace = 1;
787
                while (*srcp) {
788
                        if (*srcp == op)
789
                                in_brace++;
790
                        else if (*srcp == cl)
791
                                in_brace--;
792

    
793
                        if (in_brace == 0)
794
                                break;
795

    
796
                        if (*srcp == '\\' && *(srcp + 1) != '\0')
797
                                ++srcp;
798

    
799
                        *destp++ = *srcp++;
800
                }
801
        }
802
        *destp = '\0';
803
}
804

    
805
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
806
                                         gchar op, gchar cl)
807
{
808
        register gchar *srcp, *destp;
809
        gint in_brace;
810
        gboolean in_quote = FALSE;
811

    
812
        srcp = destp = str;
813

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

    
827
                        if (in_brace == 0)
828
                                break;
829

    
830
                        destp++;
831
                }
832
        }
833
        *destp = '\0';
834
}
835

    
836
void eliminate_quote(gchar *str, gchar quote_chr)
837
{
838
        register gchar *srcp, *destp;
839

    
840
        srcp = destp = str;
841

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

    
854
void extract_quote(gchar *str, gchar quote_chr)
855
{
856
        register gchar *p;
857

    
858
        if ((str = strchr(str, quote_chr))) {
859
                if ((p = strchr(str + 1, quote_chr))) {
860
                        *p = '\0';
861
                        memmove(str, str + 1, p - str);
862
                }
863
        }
864
}
865

    
866
void extract_quote_with_escape(gchar *str, gchar quote_chr)
867
{
868
        register gchar *sp, *dp;
869

    
870
        if ((sp = strchr(str, quote_chr))) {
871
                dp = sp;
872
                ++sp;
873
                while (*sp) {
874
                        if (*sp == quote_chr)
875
                                break;
876
                        else if (*sp == '\\' && *(sp + 1) != '\0')
877
                                ++sp;
878

    
879
                        *dp++ = *sp++;
880
                }
881
                *dp = '\0';
882
        }
883
}
884

    
885
void eliminate_address_comment(gchar *str)
886
{
887
        register gchar *srcp, *destp;
888
        gint in_brace;
889

    
890
        srcp = destp = str;
891

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

    
907
        srcp = destp = str;
908

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

    
926
gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
927
{
928
        gboolean in_quote = FALSE;
929

    
930
        while (*str) {
931
                if (*str == c && !in_quote)
932
                        return (gchar *)str;
933
                if (*str == quote_chr)
934
                        in_quote ^= TRUE;
935
                str++;
936
        }
937

    
938
        return NULL;
939
}
940

    
941
gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
942
{
943
        gboolean in_quote = FALSE;
944
        const gchar *p;
945

    
946
        p = str + strlen(str) - 1;
947
        while (p >= str) {
948
                if (*p == c && !in_quote)
949
                        return (gchar *)p;
950
                if (*p == quote_chr)
951
                        in_quote ^= TRUE;
952
                p--;
953
        }
954

    
955
        return NULL;
956
}
957

    
958
void extract_address(gchar *str)
959
{
960
        eliminate_address_comment(str);
961
        if (strchr_with_skip_quote(str, '"', '<'))
962
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
963
        g_strstrip(str);
964
}
965

    
966
void extract_list_id_str(gchar *str)
967
{
968
        if (strchr_with_skip_quote(str, '"', '<'))
969
                extract_parenthesis_with_skip_quote(str, '"', '<', '>');
970
        g_strstrip(str);
971
}
972

    
973
gchar *extract_addresses(const gchar *str)
974
{
975
        GString *new_str;
976
        GSList *addr_list, *cur;
977

    
978
        if (!str)
979
                return NULL;
980

    
981
        addr_list = address_list_append(NULL, str);
982

    
983
        new_str = g_string_new(NULL);
984

    
985
        for (cur = addr_list; cur != NULL; cur = cur->next) {
986
                g_string_append(new_str, (gchar *)cur->data);
987
                if (cur->next)
988
                        g_string_append(new_str, ", ");
989
        }
990

    
991
        slist_free_strings(addr_list);
992
        g_slist_free(addr_list);
993

    
994
        return g_string_free(new_str, FALSE);
995
}
996

    
997
gchar *normalize_address_field(const gchar *str)
998
{
999
        GString *new_str;
1000
        GSList *addr_list, *cur;
1001
        gchar *addr, *p, *q, *r;
1002
        gchar *ret_str;
1003

    
1004
        addr_list = address_list_append_orig(NULL, str);
1005

    
1006
        new_str = g_string_new(NULL);
1007

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

    
1040
                if (cur->next)
1041
                        g_string_append(new_str, ", ");
1042
        }
1043

    
1044
        slist_free_strings(addr_list);
1045
        ret_str = new_str->str;
1046
        g_string_free(new_str, FALSE);
1047

    
1048
        return ret_str;
1049
}
1050

    
1051
gboolean address_equal(const gchar *addr1, const gchar *addr2)
1052
{
1053
        gchar *addr1_, *addr2_;
1054

    
1055
        if (!addr1 || !addr2)
1056
                return FALSE;
1057

    
1058
        Xstrdup_a(addr1_, addr1, return FALSE);
1059
        Xstrdup_a(addr2_, addr2, return FALSE);
1060

    
1061
        extract_address(addr1_);
1062
        extract_address(addr2_);
1063

    
1064
        return strcmp(addr1_, addr2_) == 0;
1065
}
1066

    
1067
GSList *address_list_append_orig(GSList *addr_list, const gchar *str)
1068
{
1069
        const gchar *p = str, *q;
1070
        gchar *addr;
1071

    
1072
        if (!str) return addr_list;
1073

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

    
1090
        return addr_list;
1091
}
1092

    
1093
GSList *address_list_append(GSList *addr_list, const gchar *str)
1094
{
1095
        gchar *work;
1096
        gchar *workp;
1097

    
1098
        if (!str) return addr_list;
1099

    
1100
        Xstrdup_a(work, str, return addr_list);
1101

    
1102
        eliminate_address_comment(work);
1103
        workp = work;
1104

    
1105
        while (workp && *workp) {
1106
                gchar *p, *next;
1107

    
1108
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1109
                        *p = '\0';
1110
                        next = p + 1;
1111
                } else
1112
                        next = NULL;
1113

    
1114
                if (strchr_with_skip_quote(workp, '"', '<'))
1115
                        extract_parenthesis_with_skip_quote
1116
                                (workp, '"', '<', '>');
1117

    
1118
                g_strstrip(workp);
1119
                if (*workp)
1120
                        addr_list = g_slist_append(addr_list, g_strdup(workp));
1121

    
1122
                workp = next;
1123
        }
1124

    
1125
        return addr_list;
1126
}
1127

    
1128
GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
1129
{
1130
        const gchar *strp;
1131

    
1132
        if (!str) return msgid_list;
1133
        strp = str;
1134

    
1135
        while (strp && *strp) {
1136
                const gchar *start, *end;
1137
                gchar *msgid;
1138

    
1139
                if ((start = strchr(strp, '<')) != NULL) {
1140
                        end = strchr(start + 1, '>');
1141
                        if (!end) break;
1142
                } else
1143
                        break;
1144

    
1145
                msgid = g_strndup(start + 1, end - start - 1);
1146
                g_strstrip(msgid);
1147
                if (*msgid)
1148
                        msgid_list = g_slist_prepend(msgid_list, msgid);
1149
                else
1150
                        g_free(msgid);
1151

    
1152
                strp = end + 1;
1153
        }
1154

    
1155
        return msgid_list;
1156
}
1157

    
1158
GSList *references_list_append(GSList *msgid_list, const gchar *str)
1159
{
1160
        GSList *list;
1161

    
1162
        list = references_list_prepend(NULL, str);
1163
        list = g_slist_reverse(list);
1164
        msgid_list = g_slist_concat(msgid_list, list);
1165

    
1166
        return msgid_list;
1167
}
1168

    
1169
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
1170
{
1171
        gchar *work;
1172
        gchar *workp;
1173

    
1174
        if (!str) return group_list;
1175

    
1176
        Xstrdup_a(work, str, return group_list);
1177

    
1178
        workp = work;
1179

    
1180
        while (workp && *workp) {
1181
                gchar *p, *next;
1182

    
1183
                if ((p = strchr_with_skip_quote(workp, '"', ','))) {
1184
                        *p = '\0';
1185
                        next = p + 1;
1186
                } else
1187
                        next = NULL;
1188

    
1189
                g_strstrip(workp);
1190
                if (*workp)
1191
                        group_list = g_slist_append(group_list,
1192
                                                    g_strdup(workp));
1193

    
1194
                workp = next;
1195
        }
1196

    
1197
        return group_list;
1198
}
1199

    
1200
GList *add_history(GList *list, const gchar *str)
1201
{
1202
        GList *old;
1203

    
1204
        g_return_val_if_fail(str != NULL, list);
1205

    
1206
        old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1207
        if (old) {
1208
                g_free(old->data);
1209
                list = g_list_remove(list, old->data);
1210
        } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1211
                GList *last;
1212

    
1213
                last = g_list_last(list);
1214
                if (last) {
1215
                        g_free(last->data);
1216
                        list = g_list_remove(list, last->data);
1217
                }
1218
        }
1219

    
1220
        list = g_list_prepend(list, g_strdup(str));
1221

    
1222
        return list;
1223
}
1224

    
1225
void remove_return(gchar *str)
1226
{
1227
        register gchar *p = str;
1228

    
1229
        while (*p) {
1230
                if (*p == '\n' || *p == '\r')
1231
                        memmove(p, p + 1, strlen(p));
1232
                else
1233
                        p++;
1234
        }
1235
}
1236

    
1237
void remove_space(gchar *str)
1238
{
1239
        register gchar *p = str;
1240
        register gint spc;
1241

    
1242
        while (*p) {
1243
                spc = 0;
1244
                while (g_ascii_isspace(*(p + spc)))
1245
                        spc++;
1246
                if (spc)
1247
                        memmove(p, p + spc, strlen(p + spc) + 1);
1248
                else
1249
                        p++;
1250
        }
1251
}
1252

    
1253
void unfold_line(gchar *str)
1254
{
1255
        register gchar *p = str;
1256
        register gint spc;
1257

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

    
1271
void subst_char(gchar *str, gchar orig, gchar subst)
1272
{
1273
        register gchar *p = str;
1274

    
1275
        while (*p) {
1276
                if (*p == orig)
1277
                        *p = subst;
1278
                p++;
1279
        }
1280
}
1281

    
1282
void subst_chars(gchar *str, gchar *orig, gchar subst)
1283
{
1284
        register gchar *p = str;
1285

    
1286
        while (*p) {
1287
                if (strchr(orig, *p) != NULL)
1288
                        *p = subst;
1289
                ++p;
1290
        }
1291
}
1292

    
1293
void subst_null(gchar *str, gint len, gchar subst)
1294
{
1295
        register gchar *p = str;
1296

    
1297
        while (len--) {
1298
                if (*p == '\0')
1299
                        *p = subst;
1300
                ++p;
1301
        }
1302
}
1303

    
1304
void subst_control(gchar *str, gchar subst)
1305
{
1306
        register gchar *p = str;
1307

    
1308
        while (*p) {
1309
                if (g_ascii_iscntrl(*p))
1310
                        *p = subst;
1311
                ++p;
1312
        }
1313
}
1314

    
1315
void subst_for_filename(gchar *str)
1316
{
1317
        subst_chars(str, " \t\r\n\"'\\/:;*?<>|", '_');
1318
}
1319

    
1320
gchar *get_alt_filename(const gchar *filename, gint count)
1321
{
1322
        const gchar *ext;
1323
        gchar *alt_filename;
1324

    
1325
        ext = strrchr(filename, '.');
1326

    
1327
        if (ext) {
1328
                gchar *base;
1329

    
1330
                base = g_strndup(filename, ext - filename);
1331
                alt_filename = g_strdup_printf("%s-%d%s", base, count, ext);
1332
                g_free(base);
1333
        } else
1334
                alt_filename = g_strdup_printf("%s-%d", filename, count);
1335

    
1336
        return alt_filename;
1337
}
1338

    
1339
gboolean is_header_line(const gchar *str)
1340
{
1341
        if (str[0] == ':') return FALSE;
1342

    
1343
        while (*str != '\0' && *str != ' ') {
1344
                if (*str == ':')
1345
                        return TRUE;
1346
                str++;
1347
        }
1348

    
1349
        return FALSE;
1350
}
1351

    
1352
gboolean is_ascii_str(const gchar *str)
1353
{
1354
        const guchar *p = (const guchar *)str;
1355

    
1356
        while (*p != '\0') {
1357
                if (*p != '\t' && *p != ' ' &&
1358
                    *p != '\r' && *p != '\n' &&
1359
                    (*p < 32 || *p >= 127))
1360
                        return FALSE;
1361
                p++;
1362
        }
1363

    
1364
        return TRUE;
1365
}
1366

    
1367
gint get_quote_level(const gchar *str)
1368
{
1369
        const gchar *first_pos;
1370
        const gchar *last_pos;
1371
        const gchar *p = str;
1372
        gint quote_level = -1;
1373

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

    
1383
        while (p <= last_pos) {
1384
                while (p < last_pos) {
1385
                        if (g_ascii_isspace(*p))
1386
                                p++;
1387
                        else
1388
                                break;
1389
                }
1390

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

    
1404
                p++;
1405
        }
1406

    
1407
        return quote_level;
1408
}
1409

    
1410
gint check_line_length(const gchar *str, gint max_chars, gint *line)
1411
{
1412
        const gchar *p = str, *q;
1413
        gint cur_line = 0, len;
1414

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

    
1426
        len = strlen(p);
1427
        if (len > max_chars) {
1428
                if (line)
1429
                        *line = cur_line;
1430
                return -1;
1431
        }
1432

    
1433
        return 0;
1434
}
1435

    
1436
gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1437
{
1438
        register guint haystack_len, needle_len;
1439
        gboolean in_squote = FALSE, in_dquote = FALSE;
1440

    
1441
        haystack_len = strlen(haystack);
1442
        needle_len   = strlen(needle);
1443

    
1444
        if (haystack_len < needle_len || needle_len == 0)
1445
                return NULL;
1446

    
1447
        while (haystack_len >= needle_len) {
1448
                if (!in_squote && !in_dquote &&
1449
                    !strncmp(haystack, needle, needle_len))
1450
                        return (gchar *)haystack;
1451

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

    
1466
                haystack++;
1467
                haystack_len--;
1468
        }
1469

    
1470
        return NULL;
1471
}
1472

    
1473
gchar *strcasestr_with_skip_quote(const gchar *haystack, const gchar *needle)
1474
{
1475
        register guint haystack_len, needle_len;
1476
        gboolean in_squote = FALSE, in_dquote = FALSE;
1477

    
1478
        haystack_len = strlen(haystack);
1479
        needle_len   = strlen(needle);
1480

    
1481
        if (haystack_len < needle_len || needle_len == 0)
1482
                return NULL;
1483

    
1484
        while (haystack_len >= needle_len) {
1485
                if (!in_squote && !in_dquote &&
1486
                    !g_ascii_strncasecmp(haystack, needle, needle_len))
1487
                        return (gchar *)haystack;
1488

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

    
1503
                haystack++;
1504
                haystack_len--;
1505
        }
1506

    
1507
        return NULL;
1508
}
1509

    
1510
gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl)
1511
{
1512
        const gchar *p;
1513
        gchar quote_chr = '"';
1514
        gint in_brace;
1515
        gboolean in_quote = FALSE;
1516

    
1517
        p = str;
1518

    
1519
        if ((p = strchr_with_skip_quote(p, quote_chr, op))) {
1520
                p++;
1521
                in_brace = 1;
1522
                while (*p) {
1523
                        if (*p == op && !in_quote)
1524
                                in_brace++;
1525
                        else if (*p == cl && !in_quote)
1526
                                in_brace--;
1527
                        else if (*p == quote_chr)
1528
                                in_quote ^= TRUE;
1529

    
1530
                        if (in_brace == 0)
1531
                                return (gchar *)p;
1532

    
1533
                        p++;
1534
                }
1535
        }
1536

    
1537
        return NULL;
1538
}
1539

    
1540
gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl,
1541
                             gint max_tokens)
1542
{
1543
        GSList *string_list = NULL, *slist;
1544
        gchar **str_array;
1545
        const gchar *s_op, *s_cl;
1546
        guint i, n = 1;
1547

    
1548
        g_return_val_if_fail(str != NULL, NULL);
1549

    
1550
        if (max_tokens < 1)
1551
                max_tokens = G_MAXINT;
1552

    
1553
        s_op = strchr_with_skip_quote(str, '"', op);
1554
        if (!s_op) return NULL;
1555
        str = s_op;
1556
        s_cl = strchr_parenthesis_close(str, op, cl);
1557
        if (s_cl) {
1558
                do {
1559
                        guint len;
1560
                        gchar *new_string;
1561

    
1562
                        str++;
1563
                        len = s_cl - str;
1564
                        new_string = g_new(gchar, len + 1);
1565
                        strncpy(new_string, str, len);
1566
                        new_string[len] = 0;
1567
                        string_list = g_slist_prepend(string_list, new_string);
1568
                        n++;
1569
                        str = s_cl + 1;
1570

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

    
1585
        str_array = g_new(gchar*, n);
1586

    
1587
        i = n - 1;
1588
        str_array[i--] = NULL;
1589
        for (slist = string_list; slist; slist = slist->next)
1590
                str_array[i--] = slist->data;
1591

    
1592
        g_slist_free(string_list);
1593

    
1594
        return str_array;
1595
}
1596

    
1597
gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1598
                            gint max_tokens)
1599
{
1600
        GSList *string_list = NULL, *slist;
1601
        gchar **str_array, *s, *new_str;
1602
        guint i, n = 1, len;
1603

    
1604
        g_return_val_if_fail(str != NULL, NULL);
1605
        g_return_val_if_fail(delim != NULL, NULL);
1606

    
1607
        if (max_tokens < 1)
1608
                max_tokens = G_MAXINT;
1609

    
1610
        s = strstr_with_skip_quote(str, delim);
1611
        if (s) {
1612
                guint delimiter_len = strlen(delim);
1613

    
1614
                do {
1615
                        len = s - str;
1616
                        new_str = g_strndup(str, len);
1617

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

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

    
1644
        str_array = g_new(gchar*, n);
1645

    
1646
        i = n - 1;
1647
        str_array[i--] = NULL;
1648
        for (slist = string_list; slist; slist = slist->next)
1649
                str_array[i--] = slist->data;
1650

    
1651
        g_slist_free(string_list);
1652

    
1653
        return str_array;
1654
}
1655

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

    
1663
        g_return_val_if_fail(str != NULL, NULL);
1664

    
1665
        if (max_tokens < 1)
1666
                max_tokens = G_MAXINT;
1667

    
1668
        s = strchr_with_skip_quote(str, '"', delim);
1669
        if (s) {
1670
                do {
1671
                        len = s - str;
1672
                        tmpp = tmp = g_strndup(str, len);
1673

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

    
1688
                        string_list = g_slist_prepend(string_list, new_str);
1689
                        n++;
1690
                        str = s + 1;
1691
                        s = strchr_with_skip_quote(str, '"', delim);
1692
                } while (--max_tokens && s);
1693
        }
1694

    
1695
        if (*str) {
1696
                len = strlen(str);
1697
                tmpp = tmp = g_strdup(str);
1698

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

    
1713
                string_list = g_slist_prepend(string_list, new_str);
1714
                n++;
1715
        }
1716

    
1717
        str_array = g_new(gchar*, n);
1718

    
1719
        i = n - 1;
1720
        str_array[i--] = NULL;
1721
        for (slist = string_list; slist; slist = slist->next)
1722
                str_array[i--] = slist->data;
1723

    
1724
        g_slist_free(string_list);
1725

    
1726
        return str_array;
1727
}
1728

    
1729
gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1730
{
1731
        gchar *abbrev_group;
1732
        gchar *ap;
1733
        const gchar *p = group;
1734
        const gchar *last;
1735

    
1736
        last = group + strlen(group);
1737
        abbrev_group = ap = g_malloc(strlen(group) + 1);
1738

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

    
1751
        *ap = '\0';
1752
        return abbrev_group;
1753
}
1754

    
1755
gchar *trim_string(const gchar *str, gint len)
1756
{
1757
        const gchar *p = str;
1758
        gint mb_len;
1759
        gchar *new_str;
1760
        gint new_len = 0;
1761

    
1762
        if (!str) return NULL;
1763
        if (strlen(str) <= len)
1764
                return g_strdup(str);
1765
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1766
                return g_strdup(str);
1767

    
1768
        while (*p != '\0') {
1769
                mb_len = g_utf8_skip[*(guchar *)p];
1770
                if (mb_len == 0)
1771
                        break;
1772
                else if (new_len + mb_len > len)
1773
                        break;
1774

    
1775
                new_len += mb_len;
1776
                p += mb_len;
1777
        }
1778

    
1779
        Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1780
        return g_strconcat(new_str, "...", NULL);
1781
}
1782

    
1783
gchar *trim_string_before(const gchar *str, gint len)
1784
{
1785
        const gchar *p = str;
1786
        gint mb_len;
1787
        gint new_len;
1788

    
1789
        if (!str) return NULL;
1790
        if ((new_len = strlen(str)) <= len)
1791
                return g_strdup(str);
1792
        if (g_utf8_validate(str, -1, NULL) == FALSE)
1793
                return g_strdup(str);
1794

    
1795
        while (*p != '\0') {
1796
                mb_len = g_utf8_skip[*(guchar *)p];
1797
                if (mb_len == 0)
1798
                        break;
1799

    
1800
                new_len -= mb_len;
1801
                p += mb_len;
1802

    
1803
                if (new_len <= len)
1804
                        break;
1805
        }
1806

    
1807
        return g_strconcat("...", p, NULL);
1808
}
1809

    
1810
GList *uri_list_extract_filenames(const gchar *uri_list)
1811
{
1812
        GList *result = NULL;
1813
        gchar *file;
1814

    
1815
#if GLIB_CHECK_VERSION(2, 6, 0)
1816
        gchar **uris;
1817
        gint i;
1818

    
1819
        uris = g_uri_list_extract_uris(uri_list);
1820
        g_return_val_if_fail(uris != NULL, NULL);
1821

    
1822
        for (i = 0; uris[i] != NULL; i++) {
1823
                file = g_filename_from_uri(uris[i], NULL, NULL);
1824
                if (file)
1825
                        result = g_list_append(result, file);
1826
        }
1827

    
1828
        g_strfreev(uris);
1829

    
1830
        return result;
1831
#else
1832
        const gchar *p, *q;
1833

    
1834
        p = uri_list;
1835

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

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

    
1861
        return result;
1862
#endif
1863
}
1864

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

    
1880
#define INT_TO_HEX(hex, val)                \
1881
{                                        \
1882
        if ((val) < 10)                        \
1883
                hex = '0' + (val);        \
1884
        else                                \
1885
                hex = 'a' + (val) - 10;        \
1886
}
1887

    
1888
/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
1889
 * characters.
1890
 */
1891
static gint axtoi(const gchar *hex_str)
1892
{
1893
        gint hi, lo;
1894

    
1895
        HEX_TO_INT(hi, hex_str[0]);
1896
        HEX_TO_INT(lo, hex_str[1]);
1897

    
1898
        return (hi << 4) + lo;
1899
}
1900

    
1901
static void get_hex_str(gchar *out, guchar ch)
1902
{
1903
        gchar hex;
1904

    
1905
        INT_TO_HEX(hex, ch >> 4);
1906
        *out++ = hex;
1907
        INT_TO_HEX(hex, ch & 0x0f);
1908
        *out++ = hex;
1909
}
1910

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

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

    
1931
gint get_uri_len(const gchar *str)
1932
{
1933
        const gchar *p;
1934

    
1935
        if (is_uri_string(str)) {
1936
                for (p = str; *p != '\0'; p++) {
1937
                        if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1938
                                break;
1939
                }
1940
                return p - str;
1941
        }
1942

    
1943
        return 0;
1944
}
1945

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

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

    
1974
        *dec = '\0';
1975
}
1976

    
1977
void decode_xdigit_encoded_str(gchar *decoded, const gchar *encoded)
1978
{
1979
        gchar *dec = decoded;
1980
        const gchar *enc = encoded;
1981

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

    
1994
        *dec = '\0';
1995
}
1996

    
1997
gchar *encode_uri(const gchar *filename)
1998
{
1999
        gchar *uri;
2000

    
2001
        uri = g_filename_to_uri(filename, NULL, NULL);
2002
        if (!uri)
2003
                uri = g_strconcat("file://", filename, NULL);
2004

    
2005
        return uri;
2006
}
2007

    
2008
gchar *uriencode_for_filename(const gchar *filename)
2009
{
2010
        const gchar *p = filename;
2011
        gchar *enc, *outp;
2012

    
2013
        outp = enc = g_malloc(strlen(filename) * 3 + 1);
2014

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

    
2024
        *outp = '\0';
2025
        return enc;
2026
}
2027

    
2028
gchar *uriencode_for_mailto(const gchar *mailto)
2029
{
2030
        const gchar *p = mailto;
2031
        gchar *enc, *outp;
2032

    
2033
        outp = enc = g_malloc(strlen(mailto) * 3 + 1);
2034

    
2035
        for (p = mailto; *p != '\0'; p++) {
2036
                if (*p == '+') {
2037
                        *outp++ = '%';
2038
                        get_hex_str(outp, *p);
2039
                        outp += 2;
2040
                } else
2041
                        *outp++ = *p;
2042
        }
2043

    
2044
        *outp = '\0';
2045
        return enc;
2046
}
2047

    
2048
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
2049
                     gchar **subject, gchar **inreplyto, gchar **body)
2050
{
2051
        gchar *tmp_mailto;
2052
        gchar *p;
2053

    
2054
        Xstrdup_a(tmp_mailto, mailto, return -1);
2055

    
2056
        if (!strncmp(tmp_mailto, "mailto:", 7))
2057
                tmp_mailto += 7;
2058

    
2059
        p = strchr(tmp_mailto, '?');
2060
        if (p) {
2061
                *p = '\0';
2062
                p++;
2063
        }
2064

    
2065
        if (to && !*to) {
2066
                *to = g_malloc(strlen(tmp_mailto) + 1);
2067
                decode_uri(*to, tmp_mailto);
2068
        }
2069

    
2070
        while (p) {
2071
                gchar *field, *value;
2072

    
2073
                field = p;
2074

    
2075
                p = strchr(p, '=');
2076
                if (!p) break;
2077
                *p = '\0';
2078
                p++;
2079

    
2080
                value = p;
2081

    
2082
                p = strchr(p, '&');
2083
                if (p) {
2084
                        *p = '\0';
2085
                        p++;
2086
                }
2087

    
2088
                if (*value == '\0') continue;
2089

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

    
2111
        return 0;
2112
}
2113

    
2114
static gchar *startup_dir = NULL;
2115
static gchar *rc_dir = NULL;
2116

    
2117
void set_startup_dir(void)
2118
{
2119
#ifdef G_OS_WIN32
2120
        if (!startup_dir) {
2121
                startup_dir = g_win32_get_package_installation_directory
2122
                        (NULL, NULL);
2123
                if (startup_dir) {
2124
                        if (g_chdir(startup_dir) < 0) {
2125
                                FILE_OP_ERROR(startup_dir, "chdir");
2126
                                g_free(startup_dir);
2127
                                startup_dir = g_get_current_dir();
2128
                        }
2129
                } else
2130
                        startup_dir = g_get_current_dir();
2131
        }
2132
#else
2133
        if (!startup_dir)
2134
                startup_dir = g_get_current_dir();
2135
#endif
2136
}
2137

    
2138
void set_rc_dir(const gchar *dir)
2139
{
2140
        if (rc_dir)
2141
                g_free(rc_dir);
2142

    
2143
        if (dir) {
2144
                if (g_path_is_absolute(dir))
2145
                        rc_dir = g_strdup(dir);
2146
                else
2147
                        rc_dir = g_strconcat(get_startup_dir(),
2148
                                             G_DIR_SEPARATOR_S, dir, NULL);
2149
        } else
2150
                rc_dir = NULL;
2151
}
2152

    
2153
const gchar *get_startup_dir(void)
2154
{
2155
        if (!startup_dir)
2156
                set_startup_dir();
2157

    
2158
        return startup_dir;
2159
}
2160

    
2161
#ifdef G_OS_WIN32
2162
static gchar *get_win32_special_folder_path(gint nfolder)
2163
{
2164
        gchar *folder = NULL;
2165
        HRESULT hr;
2166

    
2167
        if (G_WIN32_HAVE_WIDECHAR_API()) {
2168
                wchar_t path[MAX_PATH + 1];
2169
                hr = SHGetFolderPathW(NULL, nfolder, NULL, 0, path);
2170
                if (hr == S_OK)
2171
                        folder = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
2172
        } else {
2173
                gchar path[MAX_PATH + 1];
2174
                hr = SHGetFolderPathA(NULL, nfolder, NULL, 0, path);
2175
                if (hr == S_OK)
2176
                        folder = g_locale_to_utf8(path, -1, NULL, NULL, NULL);
2177
        }
2178

    
2179
        return folder;
2180
}
2181
#endif
2182

    
2183
const gchar *get_home_dir(void)
2184
{
2185
#ifdef G_OS_WIN32
2186
        static const gchar *home_dir = NULL;
2187

    
2188
        if (!home_dir) {
2189
                home_dir = g_get_home_dir();
2190
                if (!home_dir)
2191
                        home_dir = "C:\\Sylpheed";
2192
        }
2193

    
2194
        return home_dir;
2195
#else
2196
        return g_get_home_dir();
2197
#endif
2198
}
2199

    
2200
const gchar *get_document_dir(void)
2201
{
2202
#ifdef G_OS_WIN32
2203
        static const gchar *document_dir = NULL;
2204
        HRESULT hr;
2205

    
2206
        if (!document_dir) {
2207
                document_dir = get_win32_special_folder_path(CSIDL_PERSONAL);
2208
                if (!document_dir)
2209
                        document_dir = get_home_dir();
2210
        }
2211

    
2212
        return document_dir;
2213
#elif defined(__APPLE__)
2214
        static const gchar *document_dir = NULL;
2215

    
2216
        if (!document_dir) {
2217
                document_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2218
                                           "Documents", NULL);
2219
        }
2220

    
2221
        return document_dir;
2222
#else
2223
        return get_home_dir();
2224
#endif
2225
}
2226

    
2227
const gchar *get_rc_dir(void)
2228
{
2229
        if (!rc_dir) {
2230
#ifdef G_OS_WIN32
2231
                gchar *appdata;
2232

    
2233
                appdata = get_win32_special_folder_path(CSIDL_APPDATA);
2234
                if (appdata)
2235
                        rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S,
2236
                                             RC_DIR, NULL);
2237
                else
2238
                        rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2239
                                             RC_DIR, NULL);
2240
                g_free(appdata);
2241
#elif defined(__APPLE__)
2242
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2243
                                     "Library", G_DIR_SEPARATOR_S,
2244
                                     "Application Support", G_DIR_SEPARATOR_S,
2245
                                     RC_DIR, NULL);
2246
#else
2247
                rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2248
                                     RC_DIR, NULL);
2249
#endif
2250
        }
2251

    
2252
        return rc_dir;
2253
}
2254

    
2255
const gchar *get_old_rc_dir(void)
2256
{
2257
        static gchar *old_rc_dir = NULL;
2258

    
2259
        if (!old_rc_dir)
2260
                old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2261
                                         OLD_RC_DIR, NULL);
2262

    
2263
        return old_rc_dir;
2264
}
2265

    
2266
const gchar *get_mail_base_dir(void)
2267
{
2268
#if defined(G_OS_WIN32) || defined(__APPLE__)
2269
        static gchar *mail_base_dir = NULL;
2270

    
2271
        if (!mail_base_dir)
2272
                mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2273
                                            "Mailboxes", NULL);
2274

    
2275
        return mail_base_dir;
2276
#else
2277
        return get_home_dir();
2278
#endif
2279
}
2280

    
2281
const gchar *get_news_cache_dir(void)
2282
{
2283
        static gchar *news_cache_dir = NULL;
2284

    
2285
        if (!news_cache_dir)
2286
                news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2287
                                             NEWS_CACHE_DIR, NULL);
2288

    
2289
        return news_cache_dir;
2290
}
2291

    
2292
const gchar *get_imap_cache_dir(void)
2293
{
2294
        static gchar *imap_cache_dir = NULL;
2295

    
2296
        if (!imap_cache_dir)
2297
                imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2298
                                             IMAP_CACHE_DIR, NULL);
2299

    
2300
        return imap_cache_dir;
2301
}
2302

    
2303
const gchar *get_mime_tmp_dir(void)
2304
{
2305
        static gchar *mime_tmp_dir = NULL;
2306

    
2307
        if (!mime_tmp_dir)
2308
                mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2309
                                           MIME_TMP_DIR, NULL);
2310

    
2311
        return mime_tmp_dir;
2312
}
2313

    
2314
const gchar *get_template_dir(void)
2315
{
2316
        static gchar *template_dir = NULL;
2317

    
2318
        if (!template_dir)
2319
                template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2320
                                           TEMPLATE_DIR, NULL);
2321

    
2322
        return template_dir;
2323
}
2324

    
2325
const gchar *get_tmp_dir(void)
2326
{
2327
        static gchar *tmp_dir = NULL;
2328

    
2329
        if (!tmp_dir)
2330
                tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2331
                                      TMP_DIR, NULL);
2332

    
2333
        return tmp_dir;
2334
}
2335

    
2336
gchar *get_tmp_file(void)
2337
{
2338
        gchar *tmp_file;
2339
        static guint32 id = 0;
2340

    
2341
        tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2342
                                   get_tmp_dir(), G_DIR_SEPARATOR, id++);
2343

    
2344
        return tmp_file;
2345
}
2346

    
2347
const gchar *get_domain_name(void)
2348
{
2349
        static gchar *domain_name = NULL;
2350

    
2351
        if (!domain_name) {
2352
                gchar buf[128] = "";
2353
                struct hostent *hp;
2354

    
2355
                if (gethostname(buf, sizeof(buf)) < 0) {
2356
                        perror("gethostname");
2357
                        domain_name = "unknown";
2358
                } else {
2359
                        buf[sizeof(buf) - 1] = '\0';
2360
                        if ((hp = my_gethostbyname(buf)) == NULL) {
2361
                                perror("gethostbyname");
2362
                                domain_name = g_strdup(buf);
2363
                        } else {
2364
                                domain_name = g_strdup(hp->h_name);
2365
                        }
2366
                }
2367

    
2368
                debug_print("domain name = %s\n", domain_name);
2369
                if (is_next_nonascii(domain_name)) {
2370
                        g_warning("invalid domain name: %s\n", domain_name);
2371
                        g_free(domain_name);
2372
                        domain_name = "unknown";
2373
                }
2374
        }
2375

    
2376
        return domain_name;
2377
}
2378

    
2379
off_t get_file_size(const gchar *file)
2380
{
2381
        struct stat s;
2382

    
2383
        if (g_stat(file, &s) < 0) {
2384
                FILE_OP_ERROR(file, "stat");
2385
                return -1;
2386
        }
2387

    
2388
        return s.st_size;
2389
}
2390

    
2391
off_t get_file_size_as_crlf(const gchar *file)
2392
{
2393
        FILE *fp;
2394
        off_t size = 0;
2395
        gchar buf[BUFFSIZE];
2396

    
2397
        if ((fp = g_fopen(file, "rb")) == NULL) {
2398
                FILE_OP_ERROR(file, "fopen");
2399
                return -1;
2400
        }
2401

    
2402
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2403
                strretchomp(buf);
2404
                size += strlen(buf) + 2;
2405
        }
2406

    
2407
        if (ferror(fp)) {
2408
                FILE_OP_ERROR(file, "fgets");
2409
                size = -1;
2410
        }
2411

    
2412
        fclose(fp);
2413

    
2414
        return size;
2415
}
2416

    
2417
off_t get_left_file_size(FILE *fp)
2418
{
2419
        glong pos;
2420
        glong end;
2421
        off_t size;
2422

    
2423
        if ((pos = ftell(fp)) < 0) {
2424
                perror("ftell");
2425
                return -1;
2426
        }
2427
        if (fseek(fp, 0L, SEEK_END) < 0) {
2428
                perror("fseek");
2429
                return -1;
2430
        }
2431
        if ((end = ftell(fp)) < 0) {
2432
                perror("fseek");
2433
                return -1;
2434
        }
2435
        size = end - pos;
2436
        if (fseek(fp, pos, SEEK_SET) < 0) {
2437
                perror("fseek");
2438
                return -1;
2439
        }
2440

    
2441
        return size;
2442
}
2443

    
2444
gint get_last_empty_line_size(FILE *fp, off_t size)
2445
{
2446
        glong pos;
2447
        gint lsize = 0;
2448
        gchar buf[4];
2449
        size_t nread;
2450

    
2451
        if (size < 4)
2452
                return -1;
2453

    
2454
        if ((pos = ftell(fp)) < 0) {
2455
                perror("ftell");
2456
                return -1;
2457
        }
2458
        if (fseek(fp, size - 4, SEEK_CUR) < 0) {
2459
                perror("fseek");
2460
                return -1;
2461
        }
2462

    
2463
        /* read last 4 bytes */
2464
        nread = fread(buf, sizeof(buf), 1, fp);
2465
        if (nread != 1) {
2466
                perror("fread");
2467
                return -1;
2468
        }
2469
        /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */
2470
        if (buf[3] == '\n') {
2471
                if (buf[2] == '\n')
2472
                        lsize = 1;
2473
                else if (buf[2] == '\r') {
2474
                        if (buf[1] == '\n')
2475
                                lsize = 2;
2476
                }
2477
        }
2478

    
2479
        if (fseek(fp, pos, SEEK_SET) < 0) {
2480
                perror("fseek");
2481
                return -1;
2482
        }
2483

    
2484
        return lsize;
2485
}
2486

    
2487
gboolean file_exist(const gchar *file, gboolean allow_fifo)
2488
{
2489
        if (file == NULL)
2490
                return FALSE;
2491

    
2492
        if (allow_fifo) {
2493
                struct stat s;
2494

    
2495
                if (g_stat(file, &s) < 0) {
2496
                        if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2497
                        return FALSE;
2498
                }
2499
                if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode))
2500
                        return TRUE;
2501
        } else {
2502
                return g_file_test(file, G_FILE_TEST_IS_REGULAR);
2503
        }
2504

    
2505
        return FALSE;
2506
}
2507

    
2508
gboolean is_dir_exist(const gchar *dir)
2509
{
2510
        if (dir == NULL)
2511
                return FALSE;
2512

    
2513
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
2514
}
2515

    
2516
gboolean is_file_entry_exist(const gchar *file)
2517
{
2518
        if (file == NULL)
2519
                return FALSE;
2520

    
2521
        return g_file_test(file, G_FILE_TEST_EXISTS);
2522
}
2523

    
2524
gboolean dirent_is_regular_file(struct dirent *d)
2525
{
2526
#ifdef HAVE_DIRENT_D_TYPE
2527
        if (d->d_type == DT_REG)
2528
                return TRUE;
2529
        else if (d->d_type != DT_UNKNOWN)
2530
                return FALSE;
2531
#endif
2532

    
2533
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2534
}
2535

    
2536
gboolean dirent_is_directory(struct dirent *d)
2537
{
2538
#ifdef HAVE_DIRENT_D_TYPE
2539
        if (d->d_type == DT_DIR)
2540
                return TRUE;
2541
        else if (d->d_type != DT_UNKNOWN)
2542
                return FALSE;
2543
#endif
2544

    
2545
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2546
}
2547

    
2548
gint change_dir(const gchar *dir)
2549
{
2550
        gchar *prevdir = NULL;
2551

    
2552
        if (debug_mode)
2553
                prevdir = g_get_current_dir();
2554

    
2555
        if (g_chdir(dir) < 0) {
2556
                FILE_OP_ERROR(dir, "chdir");
2557
                if (debug_mode) g_free(prevdir);
2558
                return -1;
2559
        } else if (debug_mode) {
2560
                gchar *cwd;
2561

    
2562
                cwd = g_get_current_dir();
2563
                if (strcmp(prevdir, cwd) != 0)
2564
                        g_print("current dir: %s\n", cwd);
2565
                g_free(cwd);
2566
                g_free(prevdir);
2567
        }
2568

    
2569
        return 0;
2570
}
2571

    
2572
gint make_dir(const gchar *dir)
2573
{
2574
        if (g_mkdir(dir, S_IRWXU) < 0) {
2575
                FILE_OP_ERROR(dir, "mkdir");
2576
                return -1;
2577
        }
2578
        if (g_chmod(dir, S_IRWXU) < 0)
2579
                FILE_OP_ERROR(dir, "chmod");
2580

    
2581
        return 0;
2582
}
2583

    
2584
gint make_dir_hier(const gchar *dir)
2585
{
2586
        gchar *parent_dir;
2587
        const gchar *p;
2588

    
2589
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2590
                parent_dir = g_strndup(dir, p - dir);
2591
                if (*parent_dir != '\0') {
2592
                        if (!is_dir_exist(parent_dir)) {
2593
                                if (make_dir(parent_dir) < 0) {
2594
                                        g_free(parent_dir);
2595
                                        return -1;
2596
                                }
2597
                        }
2598
                }
2599
                g_free(parent_dir);
2600
        }
2601

    
2602
        if (!is_dir_exist(dir)) {
2603
                if (make_dir(dir) < 0)
2604
                        return -1;
2605
        }
2606

    
2607
        return 0;
2608
}
2609

    
2610
gint remove_all_files(const gchar *dir)
2611
{
2612
        GDir *dp;
2613
        const gchar *dir_name;
2614
        gchar *prev_dir;
2615

    
2616
        prev_dir = g_get_current_dir();
2617

    
2618
        if (g_chdir(dir) < 0) {
2619
                FILE_OP_ERROR(dir, "chdir");
2620
                g_free(prev_dir);
2621
                return -1;
2622
        }
2623

    
2624
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2625
                g_warning("failed to open directory: %s\n", dir);
2626
                g_free(prev_dir);
2627
                return -1;
2628
        }
2629

    
2630
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2631
                if (g_unlink(dir_name) < 0)
2632
                        FILE_OP_ERROR(dir_name, "unlink");
2633
        }
2634

    
2635
        g_dir_close(dp);
2636

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

    
2643
        g_free(prev_dir);
2644

    
2645
        return 0;
2646
}
2647

    
2648
gint remove_numbered_files(const gchar *dir, guint first, guint last)
2649
{
2650
        GDir *dp;
2651
        const gchar *dir_name;
2652
        gchar *prev_dir;
2653
        guint file_no;
2654

    
2655
        prev_dir = g_get_current_dir();
2656

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

    
2663
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2664
                g_warning("failed to open directory: %s\n", dir);
2665
                g_free(prev_dir);
2666
                return -1;
2667
        }
2668

    
2669
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2670
                file_no = to_unumber(dir_name);
2671
                if (file_no > 0 && first <= file_no && file_no <= last) {
2672
                        if (is_dir_exist(dir_name))
2673
                                continue;
2674
                        if (g_unlink(dir_name) < 0)
2675
                                FILE_OP_ERROR(dir_name, "unlink");
2676
                }
2677
        }
2678

    
2679
        g_dir_close(dp);
2680

    
2681
        if (g_chdir(prev_dir) < 0) {
2682
                FILE_OP_ERROR(prev_dir, "chdir");
2683
                g_free(prev_dir);
2684
                return -1;
2685
        }
2686

    
2687
        g_free(prev_dir);
2688

    
2689
        return 0;
2690
}
2691

    
2692
gint remove_all_numbered_files(const gchar *dir)
2693
{
2694
        return remove_numbered_files(dir, 0, UINT_MAX);
2695
}
2696

    
2697
gint remove_expired_files(const gchar *dir, guint hours)
2698
{
2699
        GDir *dp;
2700
        const gchar *dir_name;
2701
        struct stat s;
2702
        gchar *prev_dir;
2703
        guint file_no;
2704
        time_t mtime, now, expire_time;
2705

    
2706
        prev_dir = g_get_current_dir();
2707

    
2708
        if (g_chdir(dir) < 0) {
2709
                FILE_OP_ERROR(dir, "chdir");
2710
                g_free(prev_dir);
2711
                return -1;
2712
        }
2713

    
2714
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2715
                g_warning("failed to open directory: %s\n", dir);
2716
                g_free(prev_dir);
2717
                return -1;
2718
        }
2719

    
2720
        now = time(NULL);
2721
        expire_time = hours * 60 * 60;
2722

    
2723
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2724
                file_no = to_unumber(dir_name);
2725
                if (file_no > 0) {
2726
                        if (g_stat(dir_name, &s) < 0) {
2727
                                FILE_OP_ERROR(dir_name, "stat");
2728
                                continue;
2729
                        }
2730
                        if (S_ISDIR(s.st_mode))
2731
                                continue;
2732
                        mtime = MAX(s.st_mtime, s.st_atime);
2733
                        if (now - mtime > expire_time) {
2734
                                if (g_unlink(dir_name) < 0)
2735
                                        FILE_OP_ERROR(dir_name, "unlink");
2736
                        }
2737
                }
2738
        }
2739

    
2740
        g_dir_close(dp);
2741

    
2742
        if (g_chdir(prev_dir) < 0) {
2743
                FILE_OP_ERROR(prev_dir, "chdir");
2744
                g_free(prev_dir);
2745
                return -1;
2746
        }
2747

    
2748
        g_free(prev_dir);
2749

    
2750
        return 0;
2751
}
2752

    
2753
static gint remove_dir_recursive_real(const gchar *dir)
2754
{
2755
        struct stat s;
2756
        GDir *dp;
2757
        const gchar *dir_name;
2758
        gchar *prev_dir;
2759

    
2760
        if (g_stat(dir, &s) < 0) {
2761
                FILE_OP_ERROR(dir, "stat");
2762
                if (ENOENT == errno) return 0;
2763
                return -1;
2764
        }
2765

    
2766
        if (!S_ISDIR(s.st_mode)) {
2767
                if (g_unlink(dir) < 0) {
2768
                        FILE_OP_ERROR(dir, "unlink");
2769
                        return -1;
2770
                }
2771

    
2772
                return 0;
2773
        }
2774

    
2775
        prev_dir = g_get_current_dir();
2776
        /* g_print("prev_dir = %s\n", prev_dir); */
2777

    
2778
        if (g_chdir(dir) < 0) {
2779
                FILE_OP_ERROR(dir, "chdir");
2780
                g_free(prev_dir);
2781
                return -1;
2782
        }
2783

    
2784
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2785
                g_warning("failed to open directory: %s\n", dir);
2786
                g_chdir(prev_dir);
2787
                g_free(prev_dir);
2788
                return -1;
2789
        }
2790

    
2791
        /* remove all files in the directory */
2792
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2793
                /* g_print("removing %s\n", dir_name); */
2794

    
2795
                if (is_dir_exist(dir_name)) {
2796
                        if (remove_dir_recursive_real(dir_name) < 0) {
2797
                                g_warning("can't remove directory\n");
2798
                                return -1;
2799
                        }
2800
                } else {
2801
                        if (g_unlink(dir_name) < 0)
2802
                                FILE_OP_ERROR(dir_name, "unlink");
2803
                }
2804
        }
2805

    
2806
        g_dir_close(dp);
2807

    
2808
        if (g_chdir(prev_dir) < 0) {
2809
                FILE_OP_ERROR(prev_dir, "chdir");
2810
                g_free(prev_dir);
2811
                return -1;
2812
        }
2813

    
2814
        g_free(prev_dir);
2815

    
2816
        if (g_rmdir(dir) < 0) {
2817
                if (ENOTDIR == errno) {
2818
                        if (g_unlink(dir) < 0) {
2819
                                FILE_OP_ERROR(dir, "unlink");
2820
                                return -1;
2821
                        }
2822
                } else {
2823
                        FILE_OP_ERROR(dir, "rmdir");
2824
                        return -1;
2825
                }
2826
        }
2827

    
2828
        return 0;
2829
}
2830

    
2831
gint remove_dir_recursive(const gchar *dir)
2832
{
2833
        gchar *cur_dir;
2834
        gint ret;
2835

    
2836
        debug_print("remove_dir_recursive: %s\n", dir);
2837

    
2838
        cur_dir = g_get_current_dir();
2839

    
2840
        if (g_chdir(dir) < 0) {
2841
                FILE_OP_ERROR(dir, "chdir");
2842
                ret = -1;
2843
                goto leave;
2844
        }
2845
        if (g_chdir("..") < 0) {
2846
                FILE_OP_ERROR(dir, "chdir");
2847
                ret = -1;
2848
                goto leave;
2849
        }
2850

    
2851
        ret = remove_dir_recursive_real(dir);
2852

    
2853
leave:
2854
        if (is_dir_exist(cur_dir)) {
2855
                if (g_chdir(cur_dir) < 0) {
2856
                        FILE_OP_ERROR(cur_dir, "chdir");
2857
                }
2858
        }
2859

    
2860
        g_free(cur_dir);
2861

    
2862
        return ret;
2863
}
2864

    
2865
gint rename_force(const gchar *oldpath, const gchar *newpath)
2866
{
2867
#if !defined(G_OS_UNIX) && !GLIB_CHECK_VERSION(2, 9, 1)
2868
        if (!is_file_entry_exist(oldpath)) {
2869
                errno = ENOENT;
2870
                return -1;
2871
        }
2872
        if (is_file_exist(newpath)) {
2873
                if (g_unlink(newpath) < 0)
2874
                        FILE_OP_ERROR(newpath, "unlink");
2875
        }
2876
#endif
2877
        return g_rename(oldpath, newpath);
2878
}
2879

    
2880
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2881
{
2882
#ifdef G_OS_WIN32
2883
        wchar_t *wsrc;
2884
        wchar_t *wdest;
2885
        gchar *dest_bak = NULL;
2886
        gboolean err = FALSE;
2887

    
2888
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
2889
        if (wsrc == NULL) {
2890
                return -1;
2891
        }
2892
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
2893
        if (wdest == NULL) {
2894
                g_free(wsrc);
2895
                return -1;
2896
        }
2897

    
2898
        if (keep_backup == FALSE) {
2899
                if (CopyFileW(wsrc, wdest, FALSE) == 0)
2900
                        err = TRUE;
2901
                g_free(wdest);
2902
                g_free(wsrc);
2903
                return err ? -1 : 0;
2904
        }
2905

    
2906
        if (is_file_exist(dest)) {
2907
                dest_bak = g_strconcat(dest, ".bak", NULL);
2908
                if (rename_force(dest, dest_bak) < 0) {
2909
                        FILE_OP_ERROR(dest, "rename");
2910
                        g_free(dest_bak);
2911
                        g_free(wdest);
2912
                        g_free(wsrc);
2913
                        return -1;
2914
                }
2915
        }
2916

    
2917
        if (CopyFileW(wsrc, wdest, FALSE) == 0)
2918
                err = TRUE;
2919

    
2920
        g_free(wdest);
2921
        g_free(wsrc);
2922
#else
2923
        gint srcfd, destfd;
2924
        gint n_read;
2925
        gchar buf[BUFFSIZE];
2926
        gchar *dest_bak = NULL;
2927
        gboolean err = FALSE;
2928

    
2929
        if ((srcfd = g_open(src, O_RDONLY, 0600)) < 0) {
2930
                FILE_OP_ERROR(src, "open");
2931
                return -1;
2932
        }
2933
        if (is_file_exist(dest)) {
2934
                dest_bak = g_strconcat(dest, ".bak", NULL);
2935
                if (rename_force(dest, dest_bak) < 0) {
2936
                        FILE_OP_ERROR(dest, "rename");
2937
                        close(srcfd);
2938
                        g_free(dest_bak);
2939
                        return -1;
2940
                }
2941
        }
2942

    
2943
        if ((destfd = g_open(dest, O_WRONLY | O_CREAT, 0600)) < 0) {
2944
                FILE_OP_ERROR(dest, "open");
2945
                close(srcfd);
2946
                if (dest_bak) {
2947
                        if (rename_force(dest_bak, dest) < 0)
2948
                                FILE_OP_ERROR(dest_bak, "rename");
2949
                        g_free(dest_bak);
2950
                }
2951
                return -1;
2952
        }
2953

    
2954
        while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) {
2955
                gchar *p = buf;
2956
                const gchar *endp = buf + n_read;
2957
                gint n_write;
2958

    
2959
                while (p < endp) {
2960
                        if ((n_write = write(destfd, p, endp - p)) < 0) {
2961
                                g_warning(_("writing to %s failed.\n"), dest);
2962
                                close(destfd);
2963
                                close(srcfd);
2964
                                g_unlink(dest);
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
                        p += n_write;
2973
                }
2974
        }
2975

    
2976
        if (close(destfd) < 0) {
2977
                FILE_OP_ERROR(dest, "close");
2978
                err = TRUE;
2979
        }
2980
        close(srcfd);
2981
#endif
2982

    
2983
        if (err) {
2984
                g_unlink(dest);
2985
                if (dest_bak) {
2986
                        if (rename_force(dest_bak, dest) < 0)
2987
                                FILE_OP_ERROR(dest_bak, "rename");
2988
                        g_free(dest_bak);
2989
                }
2990
                return -1;
2991
        }
2992

    
2993
        if (keep_backup == FALSE && dest_bak)
2994
                g_unlink(dest_bak);
2995

    
2996
        g_free(dest_bak);
2997

    
2998
        return 0;
2999
}
3000

    
3001
gint copy_dir(const gchar *src, const gchar *dest)
3002
{
3003
        GDir *dir;
3004
        const gchar *dir_name;
3005
        gchar *src_file;
3006
        gchar *dest_file;
3007

    
3008
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
3009
                g_warning("failed to open directory: %s\n", src);
3010
                return -1;
3011
        }
3012

    
3013
        if (make_dir_hier(dest) < 0) {
3014
                g_dir_close(dir);
3015
                return -1;
3016
        }
3017

    
3018
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
3019
                src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
3020
                dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
3021
                                        NULL);
3022
                if (is_file_exist(src_file))
3023
                        copy_file(src_file, dest_file, FALSE);
3024
                g_free(dest_file);
3025
                g_free(src_file);
3026
        }
3027

    
3028
        g_dir_close(dir);
3029

    
3030
        return 0;
3031
}
3032

    
3033
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
3034
{
3035
        if (overwrite == FALSE && is_file_entry_exist(dest)) {
3036
                g_warning("move_file(): file %s already exists.", dest);
3037
                return -1;
3038
        }
3039

    
3040
        if (rename_force(src, dest) == 0) return 0;
3041

    
3042
        if (EXDEV != errno) {
3043
                FILE_OP_ERROR(src, "rename");
3044
                return -1;
3045
        }
3046

    
3047
        if (copy_file(src, dest, FALSE) < 0) return -1;
3048

    
3049
        g_unlink(src);
3050

    
3051
        return 0;
3052
}
3053

    
3054
gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
3055
{
3056
        gint n_read;
3057
        gint bytes_left, to_read;
3058
        gchar buf[BUFSIZ];
3059

    
3060
        g_return_val_if_fail(fp != NULL, -1);
3061
        g_return_val_if_fail(dest_fp != NULL, -1);
3062

    
3063
        if (fseek(fp, offset, SEEK_SET) < 0) {
3064
                perror("fseek");
3065
                return -1;
3066
        }
3067

    
3068
        bytes_left = length;
3069
        to_read = MIN(bytes_left, sizeof(buf));
3070

    
3071
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
3072
                if (n_read < to_read && ferror(fp))
3073
                        break;
3074
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3075
                        g_warning("append_file_part: writing to file failed.\n");
3076
                        return -1;
3077
                }
3078
                bytes_left -= n_read;
3079
                if (bytes_left == 0)
3080
                        break;
3081
                to_read = MIN(bytes_left, sizeof(buf));
3082
        }
3083

    
3084
        if (ferror(fp)) {
3085
                perror("fread");
3086
                return -1;
3087
        }
3088
        if (fflush(dest_fp) == EOF) {
3089
                FILE_OP_ERROR("append_file_part", "fflush");
3090
                return -1;
3091
        }
3092

    
3093
        return 0;
3094
}
3095

    
3096
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
3097
{
3098
        FILE *dest_fp;
3099

    
3100
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3101
                FILE_OP_ERROR(dest, "fopen");
3102
                return -1;
3103
        }
3104

    
3105
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3106
                FILE_OP_ERROR(dest, "chmod");
3107
                g_warning("can't change file mode\n");
3108
        }
3109

    
3110
        if (append_file_part(fp, offset, length, dest_fp) < 0) {
3111
                g_warning("writing to %s failed.\n", dest);
3112
                fclose(dest_fp);
3113
                g_unlink(dest);
3114
                return -1;
3115
        }
3116

    
3117
        if (fclose(dest_fp) == EOF) {
3118
                FILE_OP_ERROR(dest, "fclose");
3119
                g_unlink(dest);
3120
                return -1;
3121
        }
3122

    
3123
        return 0;
3124
}
3125

    
3126
gint copy_file_stream(FILE *fp, FILE *dest_fp)
3127
{
3128
        gint n_read;
3129
        gchar buf[BUFFSIZE];
3130

    
3131
        g_return_val_if_fail(fp != NULL, -1);
3132
        g_return_val_if_fail(dest_fp != NULL, -1);
3133

    
3134
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3135
                if (n_read < sizeof(buf) && ferror(fp))
3136
                        break;
3137
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3138
                        g_warning("copy_file_stream: writing to file failed.\n");
3139
                        return -1;
3140
                }
3141
        }
3142

    
3143
        if (ferror(fp)) {
3144
                perror("fread");
3145
                return -1;
3146
        }
3147
        if (fflush(dest_fp) == EOF) {
3148
                FILE_OP_ERROR("copy_file_stream", "fflush");
3149
                return -1;
3150
        }
3151

    
3152
        return 0;
3153
}
3154

    
3155
/* convert line endings into CRLF. If the last line doesn't end with
3156
 * linebreak, add it.
3157
 */
3158
gchar *canonicalize_str(const gchar *str)
3159
{
3160
        const gchar *p;
3161
        guint new_len = 0;
3162
        gchar *out, *outp;
3163

    
3164
        for (p = str; *p != '\0'; ++p) {
3165
                if (*p != '\r') {
3166
                        ++new_len;
3167
                        if (*p == '\n')
3168
                                ++new_len;
3169
                }
3170
        }
3171
        if (p == str || *(p - 1) != '\n')
3172
                new_len += 2;
3173

    
3174
        out = outp = g_malloc(new_len + 1);
3175
        for (p = str; *p != '\0'; ++p) {
3176
                if (*p != '\r') {
3177
                        if (*p == '\n')
3178
                                *outp++ = '\r';
3179
                        *outp++ = *p;
3180
                }
3181
        }
3182
        if (p == str || *(p - 1) != '\n') {
3183
                *outp++ = '\r';
3184
                *outp++ = '\n';
3185
        }
3186
        *outp = '\0';
3187

    
3188
        return out;
3189
}
3190

    
3191
gint canonicalize_file(const gchar *src, const gchar *dest)
3192
{
3193
        FILE *src_fp, *dest_fp;
3194
        gchar buf[BUFFSIZE];
3195
        gint len;
3196
        gboolean err = FALSE;
3197
        gboolean last_linebreak = FALSE;
3198

    
3199
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3200
                FILE_OP_ERROR(src, "fopen");
3201
                return -1;
3202
        }
3203

    
3204
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3205
                FILE_OP_ERROR(dest, "fopen");
3206
                fclose(src_fp);
3207
                return -1;
3208
        }
3209

    
3210
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3211
                FILE_OP_ERROR(dest, "chmod");
3212
                g_warning("can't change file mode\n");
3213
        }
3214

    
3215
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3216
                gint r = 0;
3217

    
3218
                len = strlen(buf);
3219
                if (len == 0) break;
3220
                last_linebreak = FALSE;
3221

    
3222
                if (buf[len - 1] != '\n') {
3223
                        last_linebreak = TRUE;
3224
                        r = fputs(buf, dest_fp);
3225
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3226
                        r = fputs(buf, dest_fp);
3227
                } else {
3228
                        if (len > 1) {
3229
                                r = fwrite(buf, len - 1, 1, dest_fp);
3230
                                if (r != 1)
3231
                                        r = EOF;
3232
                        }
3233
                        if (r != EOF)
3234
                                r = fputs("\r\n", dest_fp);
3235
                }
3236

    
3237
                if (r == EOF) {
3238
                        g_warning("writing to %s failed.\n", dest);
3239
                        fclose(dest_fp);
3240
                        fclose(src_fp);
3241
                        g_unlink(dest);
3242
                        return -1;
3243
                }
3244
        }
3245

    
3246
        if (last_linebreak == TRUE) {
3247
                if (fputs("\r\n", dest_fp) == EOF)
3248
                        err = TRUE;
3249
        }
3250

    
3251
        if (ferror(src_fp)) {
3252
                FILE_OP_ERROR(src, "fgets");
3253
                err = TRUE;
3254
        }
3255
        fclose(src_fp);
3256
        if (fclose(dest_fp) == EOF) {
3257
                FILE_OP_ERROR(dest, "fclose");
3258
                err = TRUE;
3259
        }
3260

    
3261
        if (err) {
3262
                g_unlink(dest);
3263
                return -1;
3264
        }
3265

    
3266
        return 0;
3267
}
3268

    
3269
gint canonicalize_file_replace(const gchar *file)
3270
{
3271
        gchar *tmp_file;
3272

    
3273
        tmp_file = get_tmp_file();
3274

    
3275
        if (canonicalize_file(file, tmp_file) < 0) {
3276
                g_free(tmp_file);
3277
                return -1;
3278
        }
3279

    
3280
        if (move_file(tmp_file, file, TRUE) < 0) {
3281
                g_warning("can't replace %s .\n", file);
3282
                g_unlink(tmp_file);
3283
                g_free(tmp_file);
3284
                return -1;
3285
        }
3286

    
3287
        g_free(tmp_file);
3288
        return 0;
3289
}
3290

    
3291
FILE *canonicalize_file_stream(FILE *src_fp, gint *length)
3292
{
3293
        FILE *dest_fp;
3294
        gchar buf[BUFFSIZE];
3295
        gint len;
3296
        gint length_ = 0;
3297
        gboolean err = FALSE;
3298
        gboolean last_linebreak = FALSE;
3299

    
3300
        if ((dest_fp = my_tmpfile()) == NULL)
3301
                return NULL;
3302

    
3303
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3304
                gint r = 0;
3305

    
3306
                len = strlen(buf);
3307
                if (len == 0) break;
3308
                last_linebreak = FALSE;
3309

    
3310
                if (buf[len - 1] != '\n') {
3311
                        last_linebreak = TRUE;
3312
                        r = fputs(buf, dest_fp);
3313
                        length_ += len;
3314
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3315
                        r = fputs(buf, dest_fp);
3316
                        length_ += len;
3317
                } else {
3318
                        if (len > 1) {
3319
                                r = fwrite(buf, len - 1, 1, dest_fp);
3320
                                if (r != 1)
3321
                                        r = EOF;
3322
                                else
3323
                                        length_ += len - 1;
3324
                        }
3325
                        if (r != EOF) {
3326
                                r = fputs("\r\n", dest_fp);
3327
                                length_ += 2;
3328
                        }
3329
                }
3330

    
3331
                if (r == EOF) {
3332
                        g_warning("writing to temporary file failed.\n");
3333
                        fclose(dest_fp);
3334
                        return NULL;
3335
                }
3336
        }
3337

    
3338
        if (last_linebreak == TRUE) {
3339
                if (fputs("\r\n", dest_fp) == EOF)
3340
                        err = TRUE;
3341
                else
3342
                        length_ += 2;
3343
        }
3344

    
3345
        if (ferror(src_fp)) {
3346
                FILE_OP_ERROR("canonicalize_file_stream", "fgets");
3347
                err = TRUE;
3348
        }
3349
        if (fflush(dest_fp) == EOF) {
3350
                FILE_OP_ERROR("canonicalize_file_stream", "fflush");
3351
                err = TRUE;
3352
        }
3353

    
3354
        if (err) {
3355
                fclose(dest_fp);
3356
                return NULL;
3357
        }
3358

    
3359
        if (length)
3360
                *length = length_;
3361

    
3362
        rewind(dest_fp);
3363
        return dest_fp;
3364
}
3365

    
3366
gint uncanonicalize_file(const gchar *src, const gchar *dest)
3367
{
3368
        FILE *src_fp, *dest_fp;
3369
        gchar buf[BUFFSIZE];
3370
        gboolean err = FALSE;
3371

    
3372
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3373
                FILE_OP_ERROR(src, "fopen");
3374
                return -1;
3375
        }
3376

    
3377
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3378
                FILE_OP_ERROR(dest, "fopen");
3379
                fclose(src_fp);
3380
                return -1;
3381
        }
3382

    
3383
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3384
                FILE_OP_ERROR(dest, "chmod");
3385
                g_warning("can't change file mode\n");
3386
        }
3387

    
3388
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3389
                strcrchomp(buf);
3390
                if (fputs(buf, dest_fp) == EOF) {
3391
                        g_warning("writing to %s failed.\n", dest);
3392
                        fclose(dest_fp);
3393
                        fclose(src_fp);
3394
                        g_unlink(dest);
3395
                        return -1;
3396
                }
3397
        }
3398

    
3399
        if (ferror(src_fp)) {
3400
                FILE_OP_ERROR(src, "fgets");
3401
                err = TRUE;
3402
        }
3403
        fclose(src_fp);
3404
        if (fclose(dest_fp) == EOF) {
3405
                FILE_OP_ERROR(dest, "fclose");
3406
                err = TRUE;
3407
        }
3408

    
3409
        if (err) {
3410
                g_unlink(dest);
3411
                return -1;
3412
        }
3413

    
3414
        return 0;
3415
}
3416

    
3417
gint uncanonicalize_file_replace(const gchar *file)
3418
{
3419
        gchar *tmp_file;
3420

    
3421
        tmp_file = get_tmp_file();
3422

    
3423
        if (uncanonicalize_file(file, tmp_file) < 0) {
3424
                g_free(tmp_file);
3425
                return -1;
3426
        }
3427

    
3428
        if (move_file(tmp_file, file, TRUE) < 0) {
3429
                g_warning("can't replace %s .\n", file);
3430
                g_unlink(tmp_file);
3431
                g_free(tmp_file);
3432
                return -1;
3433
        }
3434

    
3435
        g_free(tmp_file);
3436
        return 0;
3437
}
3438

    
3439
gchar *normalize_newlines(const gchar *str)
3440
{
3441
        const gchar *p = str;
3442
        gchar *out, *outp;
3443

    
3444
        out = outp = g_malloc(strlen(str) + 1);
3445
        for (p = str; *p != '\0'; ++p) {
3446
                if (*p == '\r') {
3447
                        if (*(p + 1) != '\n')
3448
                                *outp++ = '\n';
3449
                } else
3450
                        *outp++ = *p;
3451
        }
3452

    
3453
        *outp = '\0';
3454

    
3455
        return out;
3456
}
3457

    
3458
gchar *strchomp_all(const gchar *str)
3459
{
3460
        const gchar *p = str;
3461
        const gchar *newline, *last;
3462
        gchar *out, *outp;
3463

    
3464
        out = outp = g_malloc(strlen(str) + 1);
3465
        while (*p != '\0') {
3466
                newline = strchr(p, '\n');
3467
                if (newline) {
3468
                        for (last = newline;
3469
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3470
                                ;
3471
                        strncpy(outp, p, last - p);
3472
                        outp += last - p;
3473

    
3474
                        if (p < newline && *(newline - 1) == '\r') {
3475
                                strncpy(outp, newline - 1, 2);
3476
                                outp += 2;
3477
                        } else {
3478
                                *outp++ = *newline;
3479
                        }
3480

    
3481
                        p = newline + 1;
3482
                } else {
3483
                        for (last = p + strlen(p);
3484
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3485
                                ;
3486
                        strncpy(outp, p, last - p);
3487
                        outp += last - p;
3488
                        break;
3489
                }
3490
        }
3491

    
3492
        *outp = '\0';
3493

    
3494
        return out;
3495
}
3496

    
3497
FILE *get_outgoing_rfc2822_file(FILE *fp)
3498
{
3499
        gchar buf[BUFFSIZE];
3500
        FILE *outfp;
3501

    
3502
        outfp = my_tmpfile();
3503
        if (!outfp) {
3504
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile");
3505
                return NULL;
3506
        }
3507

    
3508
        /* output header part */
3509
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3510
                strretchomp(buf);
3511
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3512
                        gint next;
3513

    
3514
                        for (;;) {
3515
                                next = fgetc(fp);
3516
                                if (next == EOF)
3517
                                        break;
3518
                                else if (next != ' ' && next != '\t') {
3519
                                        ungetc(next, fp);
3520
                                        break;
3521
                                }
3522
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3523
                                        break;
3524
                        }
3525
                } else {
3526
                        if (fputs(buf, outfp) == EOF)
3527
                                goto file_error;
3528
                        if (fputs("\r\n", outfp) == EOF)
3529
                                goto file_error;
3530
                        if (buf[0] == '\0')
3531
                                break;
3532
                }
3533
        }
3534

    
3535
        /* output body part */
3536
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3537
                strretchomp(buf);
3538
                if (buf[0] == '.') {
3539
                        if (fputc('.', outfp) == EOF)
3540
                                goto file_error;
3541
                }
3542
                if (fputs(buf, outfp) == EOF)
3543
                        goto file_error;
3544
                if (fputs("\r\n", outfp) == EOF)
3545
                        goto file_error;
3546
        }
3547

    
3548
        if (fflush(outfp) == EOF) {
3549
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush");
3550
                goto file_error;
3551
        }
3552

    
3553
        rewind(outfp);
3554
        return outfp;
3555

    
3556
file_error:
3557
        g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n");
3558
        fclose(outfp);
3559
        return NULL;
3560
}
3561

    
3562
gchar *get_outgoing_rfc2822_str(FILE *fp)
3563
{
3564
        gchar buf[BUFFSIZE];
3565
        GString *str;
3566
        gchar *ret;
3567

    
3568
        str = g_string_new(NULL);
3569

    
3570
        /* output header part */
3571
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3572
                strretchomp(buf);
3573
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3574
                        gint next;
3575

    
3576
                        for (;;) {
3577
                                next = fgetc(fp);
3578
                                if (next == EOF)
3579
                                        break;
3580
                                else if (next != ' ' && next != '\t') {
3581
                                        ungetc(next, fp);
3582
                                        break;
3583
                                }
3584
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3585
                                        break;
3586
                        }
3587
#if 0
3588
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
3589
                        get_rfc822_date(buf, sizeof(buf));
3590
                        g_string_append_printf(str, "Date: %s\r\n", buf);
3591
#endif
3592
                } else {
3593
                        g_string_append(str, buf);
3594
                        g_string_append(str, "\r\n");
3595
                        if (buf[0] == '\0')
3596
                                break;
3597
                }
3598
        }
3599

    
3600
        /* output body part */
3601
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3602
                strretchomp(buf);
3603
                if (buf[0] == '.')
3604
                        g_string_append_c(str, '.');
3605
                g_string_append(str, buf);
3606
                g_string_append(str, "\r\n");
3607
        }
3608

    
3609
        ret = str->str;
3610
        g_string_free(str, FALSE);
3611

    
3612
        return ret;
3613
}
3614

    
3615
/*
3616
 * Create a new boundary in a way that it is very unlikely that this
3617
 * will occur in the following text.  It would be easy to ensure
3618
 * uniqueness if everything is either quoted-printable or base64
3619
 * encoded (note that conversion is allowed), but because MIME bodies
3620
 * may be nested, it may happen that the same boundary has already
3621
 * been used. We avoid scanning the message for conflicts and hope the
3622
 * best.
3623
 *
3624
 *   boundary := 0*69<bchars> bcharsnospace
3625
 *   bchars := bcharsnospace / " "
3626
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3627
 *                    "+" / "_" / "," / "-" / "." /
3628
 *                    "/" / ":" / "=" / "?"
3629
 *
3630
 * some special characters removed because of buggy MTAs
3631
 */
3632

    
3633
gchar *generate_mime_boundary(const gchar *prefix)
3634
{
3635
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3636
                             "abcdefghijklmnopqrstuvwxyz"
3637
                             "1234567890+_./=";
3638
        gchar buf_uniq[17];
3639
        gchar buf_date[64];
3640
        gint i;
3641

    
3642
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3643
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3644
        buf_uniq[i] = '\0';
3645

    
3646
        get_rfc822_date(buf_date, sizeof(buf_date));
3647
        subst_chars(buf_date, " ,:", '_');
3648

    
3649
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
3650
                               buf_date, buf_uniq);
3651
}
3652

    
3653
gint change_file_mode_rw(FILE *fp, const gchar *file)
3654
{
3655
#ifdef G_OS_WIN32
3656
        DWORD attr;
3657
        BOOL retval;
3658

    
3659
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3660
                wchar_t *wpath;
3661

    
3662
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3663
                if (wpath == NULL)
3664
                        return -1;
3665

    
3666
                attr = GetFileAttributesW(wpath);
3667
                retval = SetFileAttributesW
3668
                        (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3669

    
3670
                g_free(wpath);
3671
        } else {
3672
                gchar *cp_path;
3673

    
3674
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3675
                if (cp_path == NULL)
3676
                        return -1;
3677

    
3678
                attr = GetFileAttributesA(cp_path);
3679
                retval = SetFileAttributesA
3680
                        (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3681

    
3682
                g_free(cp_path);
3683
        }
3684

    
3685
        if (retval)
3686
                return 0;
3687
        else
3688
                return -1;
3689
#else
3690
#if HAVE_FCHMOD
3691
        if (fp)
3692
                return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3693
        else
3694
#endif
3695
                return g_chmod(file, S_IRUSR|S_IWUSR);
3696
#endif
3697
}
3698

    
3699
#ifdef G_OS_WIN32
3700
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
3701
{
3702
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3703
                wchar_t *wpath;
3704
                wchar_t *wprefix;
3705
                wchar_t *wname;
3706
                gint save_errno;
3707
                gchar *name;
3708

    
3709
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
3710
                if (wpath == NULL) {
3711
                        errno = EINVAL;
3712
                        return NULL;
3713
                }
3714
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
3715
                if (wprefix == NULL) {
3716
                        errno = EINVAL;
3717
                        g_free(wpath);
3718
                        return NULL;
3719
                }
3720

    
3721
                wname = _wtempnam(wpath, wprefix);
3722
                save_errno = errno;
3723

    
3724
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
3725
                if (name == NULL) {
3726
                        save_errno = EINVAL;
3727
                }
3728

    
3729
                g_free(wname);
3730
                g_free(wprefix);
3731
                g_free(wpath);
3732

    
3733
                errno = save_errno;
3734
                return name;
3735
        } else {
3736
                gchar *cp_path;
3737
                gchar *cp_prefix;
3738
                gchar *cp_name;
3739
                gint save_errno;
3740
                gchar *name;
3741

    
3742
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
3743
                if (cp_path == NULL) {
3744
                        errno = EINVAL;
3745
                        return NULL;
3746
                }
3747

    
3748
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
3749
                if (cp_prefix == NULL) {
3750
                        errno = EINVAL;
3751
                        g_free(cp_path);
3752
                        return NULL;
3753
                }
3754

    
3755
                cp_name = _tempnam(cp_path, cp_prefix);
3756
                save_errno = errno;
3757

    
3758
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
3759
                if (name == NULL) {
3760
                        save_errno = EINVAL;
3761
                }
3762

    
3763
                g_free(cp_name);
3764
                g_free(cp_prefix);
3765
                g_free(cp_path);
3766

    
3767
                errno = save_errno;
3768
                return name;
3769
        }
3770
}
3771
#endif
3772

    
3773
FILE *my_tmpfile(void)
3774
{
3775
#ifdef G_OS_WIN32
3776
        const gchar *tmpdir;
3777
        gchar *fname;
3778
        gint fd;
3779
        FILE *fp;
3780

    
3781
        tmpdir = get_tmp_dir();
3782
        fname = _s_tempnam(tmpdir, "sylph");
3783
        if (!fname)
3784
                return NULL;
3785

    
3786
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
3787
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
3788
        if (fd < 0) {
3789
                g_free(fname);
3790
                return NULL;
3791
        }
3792

    
3793
        fp = fdopen(fd, "w+b");
3794
        if (!fp) {
3795
                perror("fdopen");
3796
                close(fd);
3797
        }
3798

    
3799
        g_free(fname);
3800

    
3801
        return fp;
3802
#else
3803
        const gchar suffix[] = ".XXXXXX";
3804
        const gchar *tmpdir;
3805
        guint tmplen;
3806
        const gchar *progname;
3807
        guint proglen;
3808
        gchar *fname;
3809
        gint fd;
3810
        FILE *fp;
3811

    
3812
        tmpdir = get_tmp_dir();
3813
        tmplen = strlen(tmpdir);
3814
        progname = g_get_prgname();
3815
        if (!progname)
3816
                progname = "sylph";
3817
        proglen = strlen(progname);
3818
        fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix));
3819

    
3820
        memcpy(fname, tmpdir, tmplen);
3821
        fname[tmplen] = G_DIR_SEPARATOR;
3822
        memcpy(fname + tmplen + 1, progname, proglen);
3823
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3824

    
3825
        fd = g_mkstemp(fname);
3826
        if (fd < 0) {
3827
                g_free(fname);
3828
                return tmpfile();
3829
        }
3830

    
3831
        g_unlink(fname);
3832

    
3833
        fp = fdopen(fd, "w+b");
3834
        if (!fp) {
3835
                perror("fdopen");
3836
                close(fd);
3837
        }
3838

    
3839
        g_free(fname);
3840

    
3841
        return fp;
3842
#endif
3843
}
3844

    
3845
FILE *str_open_as_stream(const gchar *str)
3846
{
3847
        FILE *fp;
3848
        size_t len;
3849

    
3850
        g_return_val_if_fail(str != NULL, NULL);
3851

    
3852
        fp = my_tmpfile();
3853
        if (!fp) {
3854
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3855
                return NULL;
3856
        }
3857

    
3858
        len = strlen(str);
3859
        if (len == 0) return fp;
3860

    
3861
        if (fwrite(str, len, 1, fp) != 1) {
3862
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3863
                fclose(fp);
3864
                return NULL;
3865
        }
3866
        if (fflush(fp) == EOF) {
3867
                FILE_OP_ERROR("str_open_as_stream", "fflush");
3868
                fclose(fp);
3869
                return NULL;
3870
        }
3871

    
3872
        rewind(fp);
3873
        return fp;
3874
}
3875

    
3876
gint str_write_to_file(const gchar *str, const gchar *file)
3877
{
3878
        FILE *fp;
3879
        size_t len;
3880

    
3881
        g_return_val_if_fail(str != NULL, -1);
3882
        g_return_val_if_fail(file != NULL, -1);
3883

    
3884
        if ((fp = g_fopen(file, "wb")) == NULL) {
3885
                FILE_OP_ERROR(file, "fopen");
3886
                return -1;
3887
        }
3888

    
3889
        len = strlen(str);
3890
        if (len == 0) {
3891
                fclose(fp);
3892
                return 0;
3893
        }
3894

    
3895
        if (fwrite(str, len, 1, fp) != 1) {
3896
                FILE_OP_ERROR(file, "fwrite");
3897
                fclose(fp);
3898
                g_unlink(file);
3899
                return -1;
3900
        }
3901

    
3902
        if (fclose(fp) == EOF) {
3903
                FILE_OP_ERROR(file, "fclose");
3904
                g_unlink(file);
3905
                return -1;
3906
        }
3907

    
3908
        return 0;
3909
}
3910

    
3911
gchar *file_read_to_str(const gchar *file)
3912
{
3913
        FILE *fp;
3914
        gchar *str;
3915

    
3916
        g_return_val_if_fail(file != NULL, NULL);
3917

    
3918
        if ((fp = g_fopen(file, "rb")) == NULL) {
3919
                FILE_OP_ERROR(file, "fopen");
3920
                return NULL;
3921
        }
3922

    
3923
        str = file_read_stream_to_str(fp);
3924

    
3925
        fclose(fp);
3926

    
3927
        return str;
3928
}
3929

    
3930
gchar *file_read_stream_to_str(FILE *fp)
3931
{
3932
        GByteArray *array;
3933
        guchar buf[BUFSIZ];
3934
        gint n_read;
3935
        gchar *str;
3936

    
3937
        g_return_val_if_fail(fp != NULL, NULL);
3938

    
3939
        array = g_byte_array_new();
3940

    
3941
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3942
                if (n_read < sizeof(buf) && ferror(fp))
3943
                        break;
3944
                g_byte_array_append(array, buf, n_read);
3945
        }
3946

    
3947
        if (ferror(fp)) {
3948
                FILE_OP_ERROR("file stream", "fread");
3949
                g_byte_array_free(array, TRUE);
3950
                return NULL;
3951
        }
3952

    
3953
        buf[0] = '\0';
3954
        g_byte_array_append(array, buf, 1);
3955
        str = (gchar *)array->data;
3956
        g_byte_array_free(array, FALSE);
3957

    
3958
        return str;
3959
}
3960

    
3961
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3962
static gchar **argv_utf8_to_locale(gchar **argv)
3963
{
3964
        gint argc = 0, i;
3965
        gchar **cp_argv;
3966

    
3967
        while (argv[argc] != NULL)
3968
                argc++;
3969

    
3970
        cp_argv = g_new(gchar *, argc + 1);
3971

    
3972
        for (i = 0; i < argc; i++) {
3973
                cp_argv[i] = g_locale_from_utf8(argv[i], -1, NULL, NULL, NULL);
3974
                if (cp_argv[i] == NULL) {
3975
                        g_warning("Failed to convert from UTF-8 to locale encoding: %s\n", argv[i]);
3976
                        g_strfreev(cp_argv);
3977
                        return NULL;
3978
                }
3979
        }
3980
        cp_argv[i] = NULL;
3981

    
3982
        return cp_argv;
3983
}
3984
#endif
3985

    
3986
gint execute_async(gchar *const argv[])
3987
{
3988
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3989
        gchar **cp_argv;
3990

    
3991
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3992

    
3993
        cp_argv = argv_utf8_to_locale((gchar **)argv);
3994
        if (!cp_argv)
3995
                return -1;
3996
        if (g_spawn_async(NULL, cp_argv, NULL, G_SPAWN_SEARCH_PATH,
3997
                          NULL, NULL, NULL, NULL) == FALSE) {
3998
                g_warning("Can't execute command: %s\n", argv[0]);
3999
                g_strfreev(cp_argv);
4000
                return -1;
4001
        }
4002
        g_strfreev(cp_argv);
4003
#else
4004
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
4005

    
4006
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
4007
                          NULL, NULL, NULL, NULL) == FALSE) {
4008
                g_warning("Can't execute command: %s\n", argv[0]);
4009
                return -1;
4010
        }
4011
#endif
4012

    
4013
        return 0;
4014
}
4015

    
4016
gint execute_sync(gchar *const argv[])
4017
{
4018
        gint status;
4019
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
4020
        gchar **cp_argv;
4021
#endif
4022

    
4023
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
4024

    
4025
#ifdef G_OS_WIN32
4026
#if !GLIB_CHECK_VERSION(2, 8, 2)
4027
        cp_argv = argv_utf8_to_locale((gchar **)argv);
4028
        if (!cp_argv)
4029
                return -1;
4030
        if (g_spawn_sync(NULL, cp_argv, NULL,
4031
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
4032
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
4033
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4034
                g_warning("Can't execute command: %s\n", argv[0]);
4035
                g_strfreev(cp_argv);
4036
                return -1;
4037
        }
4038
        g_strfreev(cp_argv);
4039
#else /* !GLIB_CHECK_VERSION */
4040
        if (g_spawn_sync(NULL, (gchar **)argv, NULL,
4041
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
4042
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
4043
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4044
                g_warning("Can't execute command: %s\n", argv[0]);
4045
                return -1;
4046
        }
4047
#endif /* !GLIB_CHECK_VERSION */
4048

    
4049
        return status;
4050
#else /* G_OS_WIN32 */
4051
        if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
4052
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
4053
                g_warning("Can't execute command: %s\n", argv[0]);
4054
                return -1;
4055
        }
4056

    
4057
        if (WIFEXITED(status))
4058
                return WEXITSTATUS(status);
4059
        else
4060
                return -1;
4061
#endif /* G_OS_WIN32 */
4062
}
4063

    
4064
gint execute_command_line(const gchar *cmdline, gboolean async)
4065
{
4066
        gchar **argv;
4067
        gint ret;
4068

    
4069
        if (debug_mode) {
4070
                gchar *utf8_cmdline;
4071

    
4072
                utf8_cmdline = g_filename_to_utf8
4073
                        (cmdline, -1, NULL, NULL, NULL);
4074
                debug_print("execute_command_line(): executing: %s\n",
4075
                            utf8_cmdline ? utf8_cmdline : cmdline);
4076
                g_free(utf8_cmdline);
4077
        }
4078

    
4079
        argv = strsplit_with_quote(cmdline, " ", 0);
4080

    
4081
        if (async)
4082
                ret = execute_async(argv);
4083
        else
4084
                ret = execute_sync(argv);
4085

    
4086
        g_strfreev(argv);
4087

    
4088
        return ret;
4089
}
4090

    
4091
#if USE_THREADS
4092
typedef struct _CmdData
4093
{
4094
        const gchar *cmdline;
4095
        volatile gint flag;
4096
        gint status;
4097
} CmdData;
4098

    
4099
static gpointer execute_command_line_async_func(gpointer data)
4100
{
4101
        CmdData *cmd_data = (CmdData *)data;
4102
        gchar **argv;
4103

    
4104
        argv = strsplit_with_quote(cmd_data->cmdline, " ", 0);
4105
        cmd_data->status = execute_sync(argv);
4106
        g_strfreev(argv);
4107

    
4108
        debug_print("execute_command_line_async_func: exec done: %s\n",
4109
                    cmd_data->cmdline);
4110
        g_atomic_int_set(&cmd_data->flag, 1);
4111
        g_main_context_wakeup(NULL);
4112

    
4113
        return GINT_TO_POINTER(0);
4114
}
4115

    
4116
gint execute_command_line_async_wait(const gchar *cmdline)
4117
{
4118
        CmdData data = {NULL, 0, 0};
4119
        GThread *thread;
4120

    
4121
        if (debug_mode) {
4122
                gchar *utf8_cmdline;
4123

    
4124
                utf8_cmdline = g_filename_to_utf8
4125
                        (cmdline, -1, NULL, NULL, NULL);
4126
                debug_print("execute_command_line(): executing: %s\n",
4127
                            utf8_cmdline ? utf8_cmdline : cmdline);
4128
                g_free(utf8_cmdline);
4129
        }
4130

    
4131
        data.cmdline = cmdline;
4132
        thread = g_thread_create(execute_command_line_async_func, &data, TRUE,
4133
                                 NULL);
4134
        if (!thread)
4135
                return -1;
4136

    
4137
        debug_print("execute_command_line_async_wait: waiting thread\n");
4138
        while (g_atomic_int_get(&data.flag) == 0)
4139
                event_loop_iterate();
4140

    
4141
        g_thread_join(thread);
4142
        debug_print("execute_command_line_async_wait: thread exited\n");
4143

    
4144
        return data.status;
4145
}
4146
#else /* USE_THREADS */
4147
gint execute_command_line_async_wait(const gchar *cmdline)
4148
{
4149
        return execute_command_line(cmdline, FALSE);
4150
}
4151
#endif /* USE_THREADS */
4152

    
4153
gint execute_open_file(const gchar *file, const gchar *content_type)
4154
{
4155
#ifdef G_OS_WIN32
4156
        g_return_val_if_fail(file != NULL, -1);
4157

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

    
4160
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4161
                wchar_t *wpath;
4162

    
4163
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4164
                if (wpath == NULL)
4165
                        return -1;
4166

    
4167
                ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL);
4168

    
4169
                g_free(wpath);
4170

    
4171
                return 0;
4172
        } else {
4173
                gchar *cp_path;
4174

    
4175
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4176
                if (cp_path == NULL)
4177
                        return -1;
4178

    
4179
                ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL);
4180

    
4181
                g_free(cp_path);
4182

    
4183
                return 0;
4184
        }
4185
#elif defined(__APPLE__)
4186
        const gchar *argv[3] = {"open", NULL, NULL};
4187

    
4188
        g_return_val_if_fail(file != NULL, -1);
4189

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

    
4192
        argv[1] = file;
4193
        execute_async(argv);
4194
#endif
4195

    
4196
        return 0;
4197
}
4198

    
4199
gint execute_print_file(const gchar *file)
4200
{
4201
        g_return_val_if_fail(file != NULL, -1);
4202

    
4203
#ifdef G_OS_WIN32
4204
        log_print("printing %s\n", file);
4205

    
4206
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4207
                wchar_t *wpath;
4208

    
4209
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4210
                if (wpath == NULL)
4211
                        return -1;
4212

    
4213
                ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL);
4214

    
4215
                g_free(wpath);
4216

    
4217
                return 0;
4218
        } else {
4219
                gchar *cp_path;
4220

    
4221
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4222
                if (cp_path == NULL)
4223
                        return -1;
4224

    
4225
                ShellExecuteA(NULL, "print", cp_path, NULL, NULL,
4226
                              SW_SHOWNORMAL);
4227

    
4228
                g_free(cp_path);
4229

    
4230
                return 0;
4231
        }
4232
#endif
4233
        return 0;
4234
}
4235

    
4236
gchar *get_command_output(const gchar *cmdline)
4237
{
4238
        gchar *child_stdout;
4239
        gint status;
4240

    
4241
        g_return_val_if_fail(cmdline != NULL, NULL);
4242

    
4243
        debug_print("get_command_output(): executing: %s\n", cmdline);
4244

    
4245
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
4246
                                      NULL) == FALSE) {
4247
                g_warning("Can't execute command: %s\n", cmdline);
4248
                return NULL;
4249
        }
4250

    
4251
        return child_stdout;
4252
}
4253

    
4254
gint open_uri(const gchar *uri, const gchar *cmdline)
4255
{
4256
        gchar buf[BUFFSIZE];
4257

    
4258
        g_return_val_if_fail(uri != NULL, -1);
4259

    
4260
#if defined(G_OS_WIN32) || defined(__APPLE__)
4261
        if (!cmdline || cmdline[0] == '\0')
4262
                return execute_open_file(uri, NULL);
4263
#endif
4264

    
4265
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
4266
                g_snprintf(buf, sizeof(buf), cmdline, uri);
4267
        else {
4268
                if (cmdline)
4269
                        g_warning("Open URI command line is invalid "
4270
                                  "(there must be only one '%%s'): %s",
4271
                                  cmdline);
4272
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
4273
        }
4274

    
4275
        execute_command_line(buf, TRUE);
4276

    
4277
        return 0;
4278
}
4279

    
4280
gint play_sound(const gchar *file, gboolean async)
4281
{
4282
#ifdef G_OS_WIN32
4283
        wchar_t *wfile;
4284
        DWORD flag = SND_FILENAME;
4285

    
4286
        wfile = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4287
        if (wfile == NULL)
4288
                return -1;
4289
        if (async)
4290
                flag |= SND_ASYNC;
4291
        else
4292
                flag |= SND_SYNC;
4293
        PlaySoundW(wfile, NULL, flag);
4294
        g_free(wfile);
4295
#endif
4296
        return 0;
4297
}
4298

    
4299
time_t remote_tzoffset_sec(const gchar *zone)
4300
{
4301
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
4302
        gchar zone3[4];
4303
        gchar *p;
4304
        gchar c;
4305
        gint iustz;
4306
        gint offset;
4307
        time_t remoteoffset;
4308

    
4309
        strncpy(zone3, zone, 3);
4310
        zone3[3] = '\0';
4311
        remoteoffset = 0;
4312

    
4313
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
4314
            (c == '+' || c == '-')) {
4315
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
4316
                if (c == '-')
4317
                        remoteoffset = -remoteoffset;
4318
        } else if (!strncmp(zone, "UT" , 2) ||
4319
                   !strncmp(zone, "GMT", 2)) {
4320
                remoteoffset = 0;
4321
        } else if (strlen(zone3) == 3) {
4322
                for (p = ustzstr; *p != '\0'; p += 3) {
4323
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
4324
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
4325
                                remoteoffset = iustz * 3600;
4326
                                break;
4327
                        }
4328
                }
4329
                if (*p == '\0')
4330
                        return -1;
4331
        } else if (strlen(zone3) == 1) {
4332
                switch (zone[0]) {
4333
                case 'Z': remoteoffset =   0; break;
4334
                case 'A': remoteoffset =  -1; break;
4335
                case 'B': remoteoffset =  -2; break;
4336
                case 'C': remoteoffset =  -3; break;
4337
                case 'D': remoteoffset =  -4; break;
4338
                case 'E': remoteoffset =  -5; break;
4339
                case 'F': remoteoffset =  -6; break;
4340
                case 'G': remoteoffset =  -7; break;
4341
                case 'H': remoteoffset =  -8; break;
4342
                case 'I': remoteoffset =  -9; break;
4343
                case 'K': remoteoffset = -10; break; /* J is not used */
4344
                case 'L': remoteoffset = -11; break;
4345
                case 'M': remoteoffset = -12; break;
4346
                case 'N': remoteoffset =   1; break;
4347
                case 'O': remoteoffset =   2; break;
4348
                case 'P': remoteoffset =   3; break;
4349
                case 'Q': remoteoffset =   4; break;
4350
                case 'R': remoteoffset =   5; break;
4351
                case 'S': remoteoffset =   6; break;
4352
                case 'T': remoteoffset =   7; break;
4353
                case 'U': remoteoffset =   8; break;
4354
                case 'V': remoteoffset =   9; break;
4355
                case 'W': remoteoffset =  10; break;
4356
                case 'X': remoteoffset =  11; break;
4357
                case 'Y': remoteoffset =  12; break;
4358
                default:  remoteoffset =   0; break;
4359
                }
4360
                remoteoffset = remoteoffset * 3600;
4361
        } else
4362
                return -1;
4363

    
4364
        return remoteoffset;
4365
}
4366

    
4367
time_t tzoffset_sec(time_t *now)
4368
{
4369
        struct tm gmt, *tmp, *lt;
4370
        gint off;
4371

    
4372
        tmp = gmtime(now);
4373
        g_return_val_if_fail(tmp != NULL, -1);
4374
        gmt = *tmp;
4375
        lt = localtime(now);
4376
        g_return_val_if_fail(lt != NULL, -1);
4377

    
4378
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4379

    
4380
        if (lt->tm_year < gmt.tm_year)
4381
                off -= 24 * 60;
4382
        else if (lt->tm_year > gmt.tm_year)
4383
                off += 24 * 60;
4384
        else if (lt->tm_yday < gmt.tm_yday)
4385
                off -= 24 * 60;
4386
        else if (lt->tm_yday > gmt.tm_yday)
4387
                off += 24 * 60;
4388

    
4389
        if (off >= 24 * 60)                /* should be impossible */
4390
                off = 23 * 60 + 59;        /* if not, insert silly value */
4391
        if (off <= -24 * 60)
4392
                off = -(23 * 60 + 59);
4393

    
4394
        return off * 60;
4395
}
4396

    
4397
/* calculate timezone offset (buf must not be less than 6 bytes) */
4398
gchar *tzoffset_buf(gchar *buf, time_t *now)
4399
{
4400
        struct tm gmt, *tmp, *lt;
4401
        gint off;
4402
        gchar sign = '+';
4403

    
4404
        tmp = gmtime(now);
4405
        g_return_val_if_fail(tmp != NULL, NULL);
4406
        gmt = *tmp;
4407
        lt = localtime(now);
4408
        g_return_val_if_fail(lt != NULL, NULL);
4409

    
4410
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4411

    
4412
        if (lt->tm_year < gmt.tm_year)
4413
                off -= 24 * 60;
4414
        else if (lt->tm_year > gmt.tm_year)
4415
                off += 24 * 60;
4416
        else if (lt->tm_yday < gmt.tm_yday)
4417
                off -= 24 * 60;
4418
        else if (lt->tm_yday > gmt.tm_yday)
4419
                off += 24 * 60;
4420

    
4421
        if (off < 0) {
4422
                sign = '-';
4423
                off = -off;
4424
        }
4425

    
4426
        if (off >= 24 * 60)                /* should be impossible */
4427
                off = 23 * 60 + 59;        /* if not, insert silly value */
4428

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

    
4431
        return buf;
4432
}
4433

    
4434
gchar *tzoffset(time_t *now)
4435
{
4436
        static gchar offset_string[6];
4437

    
4438
        return tzoffset_buf(offset_string, now);
4439
}
4440

    
4441
void get_rfc822_date(gchar *buf, gint len)
4442
{
4443
        struct tm *lt;
4444
        time_t t;
4445
        gchar day[4], mon[4];
4446
        gint dd, hh, mm, ss, yyyy;
4447
        gchar off[6];
4448

    
4449
        t = time(NULL);
4450
        lt = localtime(&t);
4451

    
4452
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
4453
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
4454
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
4455
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t));
4456
}
4457

    
4458
/* just a wrapper to suppress the warning of gcc about %c */
4459
size_t my_strftime(gchar *s, size_t max, const gchar *format,
4460
                   const struct tm *tm)
4461
{
4462
        return strftime(s, max, format, tm);
4463
}
4464

    
4465
/* UI hints */
4466

    
4467
static UIUpdateFunc ui_update_func = NULL;
4468

    
4469
void set_ui_update_func(UIUpdateFunc func)
4470
{
4471
        ui_update_func = func;
4472
}
4473

    
4474
void ui_update(void)
4475
{
4476
        if (ui_update_func)
4477
                ui_update_func();
4478
}
4479

    
4480
static EventLoopFunc event_loop_func = NULL;
4481

    
4482
void set_event_loop_func(EventLoopFunc func)
4483
{
4484
        event_loop_func = func;
4485
}
4486

    
4487
void event_loop_iterate(void)
4488
{
4489
        if (event_loop_func)
4490
                event_loop_func();
4491
        else
4492
                g_main_context_iteration(NULL, TRUE);
4493
}
4494

    
4495
static ProgressFunc progress_func = NULL;
4496

    
4497
void set_progress_func(ProgressFunc func)
4498
{
4499
        progress_func = func;
4500
}
4501

    
4502
void progress_show(gint cur, gint total)
4503
{
4504
        if (progress_func)
4505
                progress_func(cur, total);
4506
}
4507

    
4508
/* user input */
4509

    
4510
static QueryPasswordFunc query_password_func = NULL;
4511

    
4512
void set_input_query_password_func(QueryPasswordFunc func)
4513
{
4514
        query_password_func = func;
4515
}
4516

    
4517
gchar *input_query_password(const gchar *server, const gchar *user)
4518
{
4519
        if (query_password_func)
4520
                return query_password_func(server, user);
4521
        else
4522
                return NULL;
4523
}
4524

    
4525
/* logging */
4526

    
4527
static FILE *log_fp = NULL;
4528
#if USE_THREADS
4529
G_LOCK_DEFINE_STATIC(log_fp);
4530
#define S_LOCK(name)        G_LOCK(name)
4531
#define S_UNLOCK(name)        G_UNLOCK(name)
4532
#else
4533
#define S_LOCK(name)
4534
#define S_UNLOCK(name)
4535
#endif
4536

    
4537
void set_log_file(const gchar *filename)
4538
{
4539
        S_LOCK(log_fp);
4540
        if (!log_fp) {
4541
                log_fp = g_fopen(filename, "w");
4542
                if (!log_fp)
4543
                        FILE_OP_ERROR(filename, "fopen");
4544
        }
4545
        S_UNLOCK(log_fp);
4546
}
4547

    
4548
void close_log_file(void)
4549
{
4550
        S_LOCK(log_fp);
4551
        if (log_fp) {
4552
                fclose(log_fp);
4553
                log_fp = NULL;
4554
        }
4555
        S_UNLOCK(log_fp);
4556
}
4557

    
4558
static guint log_verbosity_count = 0;
4559

    
4560
void set_log_verbosity(gboolean verbose)
4561
{
4562
        if (verbose)
4563
                log_verbosity_count++;
4564
        else if (log_verbosity_count > 0)
4565
                log_verbosity_count--;
4566
}
4567

    
4568
gboolean get_debug_mode(void)
4569
{
4570
        return debug_mode;
4571
}
4572

    
4573
void set_debug_mode(gboolean enable)
4574
{
4575
        debug_mode = enable;
4576
}
4577

    
4578
static void log_dummy_func(const gchar *str)
4579
{
4580
}
4581

    
4582
static void log_dummy_flush_func(void)
4583
{
4584
}
4585

    
4586
static LogFunc log_print_ui_func = log_dummy_func;
4587
static LogFunc log_message_ui_func = log_dummy_func;
4588
static LogFunc log_warning_ui_func = log_dummy_func;
4589
static LogFunc log_error_ui_func = log_dummy_func;
4590
static LogFlushFunc log_flush_ui_func = log_dummy_flush_func;
4591

    
4592
static LogFunc log_show_status_func = log_dummy_func;
4593

    
4594
void set_log_ui_func(LogFunc print_func, LogFunc message_func,
4595
                     LogFunc warning_func, LogFunc error_func)
4596
{
4597
        log_print_ui_func = print_func;
4598
        log_message_ui_func = message_func;
4599
        log_warning_ui_func = warning_func;
4600
        log_error_ui_func = error_func;
4601
}
4602

    
4603
void set_log_ui_func_full(LogFunc print_func, LogFunc message_func,
4604
                          LogFunc warning_func, LogFunc error_func,
4605
                          LogFlushFunc flush_func)
4606
{
4607
        set_log_ui_func(print_func, message_func, warning_func, error_func);
4608
        log_flush_ui_func = flush_func;
4609
}
4610

    
4611
void set_log_show_status_func(LogFunc status_func)
4612
{
4613
        log_show_status_func = status_func;
4614
}
4615

    
4616
void debug_print(const gchar *format, ...)
4617
{
4618
        va_list args;
4619
        gchar buf[BUFFSIZE];
4620

    
4621
        if (!debug_mode) return;
4622

    
4623
        va_start(args, format);
4624
        g_vsnprintf(buf, sizeof(buf), format, args);
4625
        va_end(args);
4626

    
4627
        g_print("%s", buf);
4628
}
4629

    
4630
void status_print(const gchar *format, ...)
4631
{
4632
        va_list args;
4633
        gchar buf[BUFFSIZE];
4634

    
4635
        va_start(args, format);
4636
        g_vsnprintf(buf, sizeof(buf), format, args);
4637
        va_end(args);
4638

    
4639
        log_show_status_func(buf);
4640
}
4641

    
4642
#define TIME_LEN        11
4643

    
4644
void log_write(const gchar *str, const gchar *prefix)
4645
{
4646
        S_LOCK(log_fp);
4647

    
4648
        if (log_fp) {
4649
                gchar buf[TIME_LEN + 1];
4650
                time_t t;
4651

    
4652
                time(&t);
4653
                strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4654

    
4655
                fputs(buf, log_fp);
4656
                if (prefix)
4657
                        fputs(prefix, log_fp);
4658
                fputs(str, log_fp);
4659
                fflush(log_fp);
4660
        }
4661

    
4662
        S_UNLOCK(log_fp);
4663
}
4664

    
4665
void log_print(const gchar *format, ...)
4666
{
4667
        va_list args;
4668
        gchar buf[BUFFSIZE + TIME_LEN];
4669
        time_t t;
4670

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

    
4674
        va_start(args, format);
4675
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4676
        va_end(args);
4677

    
4678
        if (debug_mode) g_print("%s", buf);
4679
        log_print_ui_func(buf);
4680
        S_LOCK(log_fp);
4681
        if (log_fp) {
4682
                fputs(buf, log_fp);
4683
                fflush(log_fp);
4684
        }
4685
        S_UNLOCK(log_fp);
4686
        if (log_verbosity_count)
4687
                log_show_status_func(buf + TIME_LEN);
4688
}
4689

    
4690
void log_message(const gchar *format, ...)
4691
{
4692
        va_list args;
4693
        gchar buf[BUFFSIZE + TIME_LEN];
4694
        time_t t;
4695

    
4696
        time(&t);
4697
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4698

    
4699
        va_start(args, format);
4700
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4701
        va_end(args);
4702

    
4703
        if (debug_mode) g_message("%s", buf + TIME_LEN);
4704
        log_message_ui_func(buf + TIME_LEN);
4705
        S_LOCK(log_fp);
4706
        if (log_fp) {
4707
                fwrite(buf, TIME_LEN, 1, log_fp);
4708
                fputs("* message: ", log_fp);
4709
                fputs(buf + TIME_LEN, log_fp);
4710
                fflush(log_fp);
4711
        }
4712
        S_UNLOCK(log_fp);
4713
        log_show_status_func(buf + TIME_LEN);
4714
}
4715

    
4716
void log_warning(const gchar *format, ...)
4717
{
4718
        va_list args;
4719
        gchar buf[BUFFSIZE + TIME_LEN];
4720
        time_t t;
4721

    
4722
        time(&t);
4723
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4724

    
4725
        va_start(args, format);
4726
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4727
        va_end(args);
4728

    
4729
        g_warning("%s", buf);
4730
        log_warning_ui_func(buf + TIME_LEN);
4731
        S_LOCK(log_fp);
4732
        if (log_fp) {
4733
                fwrite(buf, TIME_LEN, 1, log_fp);
4734
                fputs("** warning: ", log_fp);
4735
                fputs(buf + TIME_LEN, log_fp);
4736
                fflush(log_fp);
4737
        }
4738
        S_UNLOCK(log_fp);
4739
}
4740

    
4741
void log_error(const gchar *format, ...)
4742
{
4743
        va_list args;
4744
        gchar buf[BUFFSIZE + TIME_LEN];
4745
        time_t t;
4746

    
4747
        time(&t);
4748
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4749

    
4750
        va_start(args, format);
4751
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4752
        va_end(args);
4753

    
4754
        g_warning("%s", buf);
4755
        log_error_ui_func(buf + TIME_LEN);
4756
        S_LOCK(log_fp);
4757
        if (log_fp) {
4758
                fwrite(buf, TIME_LEN, 1, log_fp);
4759
                fputs("*** error: ", log_fp);
4760
                fputs(buf + TIME_LEN, log_fp);
4761
                fflush(log_fp);
4762
        }
4763
        S_UNLOCK(log_fp);
4764
}
4765

    
4766
void log_flush(void)
4767
{
4768
        S_LOCK(log_fp);
4769
        if (log_fp)
4770
                fflush(log_fp);
4771
        S_UNLOCK(log_fp);
4772
        log_flush_ui_func();
4773
}