Statistics
| Branch: | Tag: | Revision:

root / libsylph / utils.c @ 8d7dcace

History | View | Annotate | Download (88.4 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2010 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

    
57
#define BUFFSIZE        8192
58

    
59
static gboolean debug_mode = FALSE;
60

    
61

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

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

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

    
80
                g_free(wpath);
81

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

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

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

    
98
                g_free(cp_path);
99

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

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

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

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

    
125
                g_free(wpath);
126

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

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

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

    
143
                g_free(cp_path);
144

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
268
        return h;
269
}
270

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

    
276
        g_return_if_fail(array != NULL);
277

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

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

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

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

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

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

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

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

    
313
        return atoi(nstr);
314
}
315

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

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

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

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

    
331
        return (guint)val;
332
}
333

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

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

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

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

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

    
367
        return buf;
368
}
369

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

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

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

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

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

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

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

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

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

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

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

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

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

    
441
        return FALSE;
442
}
443

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

    
449
        if (!*str) return str;
450

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

    
456
        return str;
457
}
458

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

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

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

    
470
        return str;
471
}
472

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

    
478
        if (!*str) return str;
479

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

    
486
        return str;
487
}
488

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

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

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

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

    
509
        return NULL;
510
}
511

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

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

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

    
535
        return NULL;
536
}
537

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

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

    
548
        return dest;
549
}
550

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

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

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

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

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

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

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

    
584
        return n;
585
}
586

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

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

    
606
        return FALSE;
607
}
608

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

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

    
622
        return p - s;
623
}
624

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
678
        g_strstrip(str);
679

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

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

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

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

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

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

    
728
        srcp = destp = str;
729

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

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

    
752
        srcp = destp = str;
753

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

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

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

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

    
779
        srcp = destp = str;
780

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

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

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

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

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

    
811
        srcp = destp = str;
812

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

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

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

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

    
839
        srcp = destp = str;
840

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

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

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

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

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

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

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

    
889
        srcp = destp = str;
890

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

    
906
        srcp = destp = str;
907

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

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

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

    
937
        return NULL;
938
}
939

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

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

    
954
        return NULL;
955
}
956

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

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

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

    
977
        if (!str)
978
                return NULL;
979

    
980
        addr_list = address_list_append(NULL, str);
981

    
982
        new_str = g_string_new(NULL);
983

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

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

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

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

    
1003
        addr_list = address_list_append_orig(NULL, str);
1004

    
1005
        new_str = g_string_new(NULL);
1006

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

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

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

    
1047
        return ret_str;
1048
}
1049

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

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

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

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

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

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

    
1071
        if (!str) return addr_list;
1072

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

    
1089
        return addr_list;
1090
}
1091

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

    
1097
        if (!str) return addr_list;
1098

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

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

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

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

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

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

    
1121
                workp = next;
1122
        }
1123

    
1124
        return addr_list;
1125
}
1126

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

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

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

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

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

    
1151
                strp = end + 1;
1152
        }
1153

    
1154
        return msgid_list;
1155
}
1156

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

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

    
1165
        return msgid_list;
1166
}
1167

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

    
1173
        if (!str) return group_list;
1174

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

    
1177
        workp = work;
1178

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

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

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

    
1193
                workp = next;
1194
        }
1195

    
1196
        return group_list;
1197
}
1198

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

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

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

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

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

    
1221
        return list;
1222
}
1223

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1335
        return alt_filename;
1336
}
1337

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

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

    
1348
        return FALSE;
1349
}
1350

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

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

    
1363
        return TRUE;
1364
}
1365

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

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

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

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

    
1403
                p++;
1404
        }
1405

    
1406
        return quote_level;
1407
}
1408

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

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

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

    
1432
        return 0;
1433
}
1434

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

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

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

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

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

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

    
1469
        return NULL;
1470
}
1471

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

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

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

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

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

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

    
1506
        return NULL;
1507
}
1508

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

    
1516
        p = str;
1517

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

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

    
1532
                        p++;
1533
                }
1534
        }
1535

    
1536
        return NULL;
1537
}
1538

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

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

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

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

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

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

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

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

    
1591
        g_slist_free(string_list);
1592

    
1593
        return str_array;
1594
}
1595

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

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

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

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

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

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

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

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

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

    
1650
        g_slist_free(string_list);
1651

    
1652
        return str_array;
1653
}
1654

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

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

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

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

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

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

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

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

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

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

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

    
1723
        g_slist_free(string_list);
1724

    
1725
        return str_array;
1726
}
1727

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1827
        g_strfreev(uris);
1828

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

    
1833
        p = uri_list;
1834

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

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

    
1860
        return result;
1861
#endif
1862
}
1863

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

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

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

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

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

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

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

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

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

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

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

    
1942
        return 0;
1943
}
1944

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

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

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

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

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

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

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

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

    
2004
        return uri;
2005
}
2006

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2072
                field = p;
2073

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

    
2079
                value = p;
2080

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

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

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

    
2110
        return 0;
2111
}
2112

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

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

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

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

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

    
2157
        return startup_dir;
2158
}
2159

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

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

    
2178
        return folder;
2179
}
2180
#endif
2181

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

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

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

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

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

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

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

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

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

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

    
2251
        return rc_dir;
2252
}
2253

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

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

    
2262
        return old_rc_dir;
2263
}
2264

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

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

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

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

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

    
2288
        return news_cache_dir;
2289
}
2290

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

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

    
2299
        return imap_cache_dir;
2300
}
2301

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

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

    
2310
        return mime_tmp_dir;
2311
}
2312

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

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

    
2321
        return template_dir;
2322
}
2323

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

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

    
2332
        return tmp_dir;
2333
}
2334

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

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

    
2343
        return tmp_file;
2344
}
2345

    
2346
off_t get_file_size(const gchar *file)
2347
{
2348
        struct stat s;
2349

    
2350
        if (g_stat(file, &s) < 0) {
2351
                FILE_OP_ERROR(file, "stat");
2352
                return -1;
2353
        }
2354

    
2355
        return s.st_size;
2356
}
2357

    
2358
off_t get_file_size_as_crlf(const gchar *file)
2359
{
2360
        FILE *fp;
2361
        off_t size = 0;
2362
        gchar buf[BUFFSIZE];
2363

    
2364
        if ((fp = g_fopen(file, "rb")) == NULL) {
2365
                FILE_OP_ERROR(file, "fopen");
2366
                return -1;
2367
        }
2368

    
2369
        while (fgets(buf, sizeof(buf), fp) != NULL) {
2370
                strretchomp(buf);
2371
                size += strlen(buf) + 2;
2372
        }
2373

    
2374
        if (ferror(fp)) {
2375
                FILE_OP_ERROR(file, "fgets");
2376
                size = -1;
2377
        }
2378

    
2379
        fclose(fp);
2380

    
2381
        return size;
2382
}
2383

    
2384
off_t get_left_file_size(FILE *fp)
2385
{
2386
        glong pos;
2387
        glong end;
2388
        off_t size;
2389

    
2390
        if ((pos = ftell(fp)) < 0) {
2391
                perror("ftell");
2392
                return -1;
2393
        }
2394
        if (fseek(fp, 0L, SEEK_END) < 0) {
2395
                perror("fseek");
2396
                return -1;
2397
        }
2398
        if ((end = ftell(fp)) < 0) {
2399
                perror("fseek");
2400
                return -1;
2401
        }
2402
        size = end - pos;
2403
        if (fseek(fp, pos, SEEK_SET) < 0) {
2404
                perror("fseek");
2405
                return -1;
2406
        }
2407

    
2408
        return size;
2409
}
2410

    
2411
gint get_last_empty_line_size(FILE *fp, off_t size)
2412
{
2413
        glong pos;
2414
        gint lsize = 0;
2415
        gchar buf[4];
2416
        size_t nread;
2417

    
2418
        if (size < 4)
2419
                return -1;
2420

    
2421
        if ((pos = ftell(fp)) < 0) {
2422
                perror("ftell");
2423
                return -1;
2424
        }
2425
        if (fseek(fp, size - 4, SEEK_CUR) < 0) {
2426
                perror("fseek");
2427
                return -1;
2428
        }
2429

    
2430
        /* read last 4 bytes */
2431
        nread = fread(buf, sizeof(buf), 1, fp);
2432
        if (nread != 1) {
2433
                perror("fread");
2434
                return -1;
2435
        }
2436
        /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */
2437
        if (buf[3] == '\n') {
2438
                if (buf[2] == '\n')
2439
                        lsize = 1;
2440
                else if (buf[2] == '\r') {
2441
                        if (buf[1] == '\n')
2442
                                lsize = 2;
2443
                }
2444
        }
2445

    
2446
        if (fseek(fp, pos, SEEK_SET) < 0) {
2447
                perror("fseek");
2448
                return -1;
2449
        }
2450

    
2451
        return lsize;
2452
}
2453

    
2454
gboolean file_exist(const gchar *file, gboolean allow_fifo)
2455
{
2456
        if (file == NULL)
2457
                return FALSE;
2458

    
2459
        if (allow_fifo) {
2460
                struct stat s;
2461

    
2462
                if (g_stat(file, &s) < 0) {
2463
                        if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2464
                        return FALSE;
2465
                }
2466
                if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode))
2467
                        return TRUE;
2468
        } else {
2469
                return g_file_test(file, G_FILE_TEST_IS_REGULAR);
2470
        }
2471

    
2472
        return FALSE;
2473
}
2474

    
2475
gboolean is_dir_exist(const gchar *dir)
2476
{
2477
        if (dir == NULL)
2478
                return FALSE;
2479

    
2480
        return g_file_test(dir, G_FILE_TEST_IS_DIR);
2481
}
2482

    
2483
gboolean is_file_entry_exist(const gchar *file)
2484
{
2485
        if (file == NULL)
2486
                return FALSE;
2487

    
2488
        return g_file_test(file, G_FILE_TEST_EXISTS);
2489
}
2490

    
2491
gboolean dirent_is_regular_file(struct dirent *d)
2492
{
2493
#ifdef HAVE_DIRENT_D_TYPE
2494
        if (d->d_type == DT_REG)
2495
                return TRUE;
2496
        else if (d->d_type != DT_UNKNOWN)
2497
                return FALSE;
2498
#endif
2499

    
2500
        return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2501
}
2502

    
2503
gboolean dirent_is_directory(struct dirent *d)
2504
{
2505
#ifdef HAVE_DIRENT_D_TYPE
2506
        if (d->d_type == DT_DIR)
2507
                return TRUE;
2508
        else if (d->d_type != DT_UNKNOWN)
2509
                return FALSE;
2510
#endif
2511

    
2512
        return g_file_test(d->d_name, G_FILE_TEST_IS_DIR);
2513
}
2514

    
2515
gint change_dir(const gchar *dir)
2516
{
2517
        gchar *prevdir = NULL;
2518

    
2519
        if (debug_mode)
2520
                prevdir = g_get_current_dir();
2521

    
2522
        if (g_chdir(dir) < 0) {
2523
                FILE_OP_ERROR(dir, "chdir");
2524
                if (debug_mode) g_free(prevdir);
2525
                return -1;
2526
        } else if (debug_mode) {
2527
                gchar *cwd;
2528

    
2529
                cwd = g_get_current_dir();
2530
                if (strcmp(prevdir, cwd) != 0)
2531
                        g_print("current dir: %s\n", cwd);
2532
                g_free(cwd);
2533
                g_free(prevdir);
2534
        }
2535

    
2536
        return 0;
2537
}
2538

    
2539
gint make_dir(const gchar *dir)
2540
{
2541
        if (g_mkdir(dir, S_IRWXU) < 0) {
2542
                FILE_OP_ERROR(dir, "mkdir");
2543
                return -1;
2544
        }
2545
        if (g_chmod(dir, S_IRWXU) < 0)
2546
                FILE_OP_ERROR(dir, "chmod");
2547

    
2548
        return 0;
2549
}
2550

    
2551
gint make_dir_hier(const gchar *dir)
2552
{
2553
        gchar *parent_dir;
2554
        const gchar *p;
2555

    
2556
        for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2557
                parent_dir = g_strndup(dir, p - dir);
2558
                if (*parent_dir != '\0') {
2559
                        if (!is_dir_exist(parent_dir)) {
2560
                                if (make_dir(parent_dir) < 0) {
2561
                                        g_free(parent_dir);
2562
                                        return -1;
2563
                                }
2564
                        }
2565
                }
2566
                g_free(parent_dir);
2567
        }
2568

    
2569
        if (!is_dir_exist(dir)) {
2570
                if (make_dir(dir) < 0)
2571
                        return -1;
2572
        }
2573

    
2574
        return 0;
2575
}
2576

    
2577
gint remove_all_files(const gchar *dir)
2578
{
2579
        GDir *dp;
2580
        const gchar *dir_name;
2581
        gchar *prev_dir;
2582

    
2583
        prev_dir = g_get_current_dir();
2584

    
2585
        if (g_chdir(dir) < 0) {
2586
                FILE_OP_ERROR(dir, "chdir");
2587
                g_free(prev_dir);
2588
                return -1;
2589
        }
2590

    
2591
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2592
                g_warning("failed to open directory: %s\n", dir);
2593
                g_free(prev_dir);
2594
                return -1;
2595
        }
2596

    
2597
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2598
                if (g_unlink(dir_name) < 0)
2599
                        FILE_OP_ERROR(dir_name, "unlink");
2600
        }
2601

    
2602
        g_dir_close(dp);
2603

    
2604
        if (g_chdir(prev_dir) < 0) {
2605
                FILE_OP_ERROR(prev_dir, "chdir");
2606
                g_free(prev_dir);
2607
                return -1;
2608
        }
2609

    
2610
        g_free(prev_dir);
2611

    
2612
        return 0;
2613
}
2614

    
2615
gint remove_numbered_files(const gchar *dir, guint first, guint last)
2616
{
2617
        GDir *dp;
2618
        const gchar *dir_name;
2619
        gchar *prev_dir;
2620
        guint file_no;
2621

    
2622
        prev_dir = g_get_current_dir();
2623

    
2624
        if (g_chdir(dir) < 0) {
2625
                FILE_OP_ERROR(dir, "chdir");
2626
                g_free(prev_dir);
2627
                return -1;
2628
        }
2629

    
2630
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2631
                g_warning("failed to open directory: %s\n", dir);
2632
                g_free(prev_dir);
2633
                return -1;
2634
        }
2635

    
2636
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2637
                file_no = to_unumber(dir_name);
2638
                if (file_no > 0 && first <= file_no && file_no <= last) {
2639
                        if (is_dir_exist(dir_name))
2640
                                continue;
2641
                        if (g_unlink(dir_name) < 0)
2642
                                FILE_OP_ERROR(dir_name, "unlink");
2643
                }
2644
        }
2645

    
2646
        g_dir_close(dp);
2647

    
2648
        if (g_chdir(prev_dir) < 0) {
2649
                FILE_OP_ERROR(prev_dir, "chdir");
2650
                g_free(prev_dir);
2651
                return -1;
2652
        }
2653

    
2654
        g_free(prev_dir);
2655

    
2656
        return 0;
2657
}
2658

    
2659
gint remove_all_numbered_files(const gchar *dir)
2660
{
2661
        return remove_numbered_files(dir, 0, UINT_MAX);
2662
}
2663

    
2664
gint remove_expired_files(const gchar *dir, guint hours)
2665
{
2666
        GDir *dp;
2667
        const gchar *dir_name;
2668
        struct stat s;
2669
        gchar *prev_dir;
2670
        guint file_no;
2671
        time_t mtime, now, expire_time;
2672

    
2673
        prev_dir = g_get_current_dir();
2674

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

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

    
2687
        now = time(NULL);
2688
        expire_time = hours * 60 * 60;
2689

    
2690
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2691
                file_no = to_unumber(dir_name);
2692
                if (file_no > 0) {
2693
                        if (g_stat(dir_name, &s) < 0) {
2694
                                FILE_OP_ERROR(dir_name, "stat");
2695
                                continue;
2696
                        }
2697
                        if (S_ISDIR(s.st_mode))
2698
                                continue;
2699
                        mtime = MAX(s.st_mtime, s.st_atime);
2700
                        if (now - mtime > expire_time) {
2701
                                if (g_unlink(dir_name) < 0)
2702
                                        FILE_OP_ERROR(dir_name, "unlink");
2703
                        }
2704
                }
2705
        }
2706

    
2707
        g_dir_close(dp);
2708

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

    
2715
        g_free(prev_dir);
2716

    
2717
        return 0;
2718
}
2719

    
2720
static gint remove_dir_recursive_real(const gchar *dir)
2721
{
2722
        struct stat s;
2723
        GDir *dp;
2724
        const gchar *dir_name;
2725
        gchar *prev_dir;
2726

    
2727
        if (g_stat(dir, &s) < 0) {
2728
                FILE_OP_ERROR(dir, "stat");
2729
                if (ENOENT == errno) return 0;
2730
                return -1;
2731
        }
2732

    
2733
        if (!S_ISDIR(s.st_mode)) {
2734
                if (g_unlink(dir) < 0) {
2735
                        FILE_OP_ERROR(dir, "unlink");
2736
                        return -1;
2737
                }
2738

    
2739
                return 0;
2740
        }
2741

    
2742
        prev_dir = g_get_current_dir();
2743
        /* g_print("prev_dir = %s\n", prev_dir); */
2744

    
2745
        if (g_chdir(dir) < 0) {
2746
                FILE_OP_ERROR(dir, "chdir");
2747
                g_free(prev_dir);
2748
                return -1;
2749
        }
2750

    
2751
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2752
                g_warning("failed to open directory: %s\n", dir);
2753
                g_chdir(prev_dir);
2754
                g_free(prev_dir);
2755
                return -1;
2756
        }
2757

    
2758
        /* remove all files in the directory */
2759
        while ((dir_name = g_dir_read_name(dp)) != NULL) {
2760
                /* g_print("removing %s\n", dir_name); */
2761

    
2762
                if (is_dir_exist(dir_name)) {
2763
                        if (remove_dir_recursive_real(dir_name) < 0) {
2764
                                g_warning("can't remove directory\n");
2765
                                return -1;
2766
                        }
2767
                } else {
2768
                        if (g_unlink(dir_name) < 0)
2769
                                FILE_OP_ERROR(dir_name, "unlink");
2770
                }
2771
        }
2772

    
2773
        g_dir_close(dp);
2774

    
2775
        if (g_chdir(prev_dir) < 0) {
2776
                FILE_OP_ERROR(prev_dir, "chdir");
2777
                g_free(prev_dir);
2778
                return -1;
2779
        }
2780

    
2781
        g_free(prev_dir);
2782

    
2783
        if (g_rmdir(dir) < 0) {
2784
                if (ENOTDIR == errno) {
2785
                        if (g_unlink(dir) < 0) {
2786
                                FILE_OP_ERROR(dir, "unlink");
2787
                                return -1;
2788
                        }
2789
                } else {
2790
                        FILE_OP_ERROR(dir, "rmdir");
2791
                        return -1;
2792
                }
2793
        }
2794

    
2795
        return 0;
2796
}
2797

    
2798
gint remove_dir_recursive(const gchar *dir)
2799
{
2800
        gchar *cur_dir;
2801
        gint ret;
2802

    
2803
        debug_print("remove_dir_recursive: %s\n", dir);
2804

    
2805
        cur_dir = g_get_current_dir();
2806

    
2807
        if (g_chdir(dir) < 0) {
2808
                FILE_OP_ERROR(dir, "chdir");
2809
                ret = -1;
2810
                goto leave;
2811
        }
2812
        if (g_chdir("..") < 0) {
2813
                FILE_OP_ERROR(dir, "chdir");
2814
                ret = -1;
2815
                goto leave;
2816
        }
2817

    
2818
        ret = remove_dir_recursive_real(dir);
2819

    
2820
leave:
2821
        if (is_dir_exist(cur_dir)) {
2822
                if (g_chdir(cur_dir) < 0) {
2823
                        FILE_OP_ERROR(cur_dir, "chdir");
2824
                }
2825
        }
2826

    
2827
        g_free(cur_dir);
2828

    
2829
        return ret;
2830
}
2831

    
2832
gint rename_force(const gchar *oldpath, const gchar *newpath)
2833
{
2834
#if !defined(G_OS_UNIX) && !GLIB_CHECK_VERSION(2, 9, 1)
2835
        if (!is_file_entry_exist(oldpath)) {
2836
                errno = ENOENT;
2837
                return -1;
2838
        }
2839
        if (is_file_exist(newpath)) {
2840
                if (g_unlink(newpath) < 0)
2841
                        FILE_OP_ERROR(newpath, "unlink");
2842
        }
2843
#endif
2844
        return g_rename(oldpath, newpath);
2845
}
2846

    
2847
gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2848
{
2849
#ifdef G_OS_WIN32
2850
        wchar_t *wsrc;
2851
        wchar_t *wdest;
2852
        gchar *dest_bak = NULL;
2853
        gboolean err = FALSE;
2854

    
2855
        wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL);
2856
        if (wsrc == NULL) {
2857
                return -1;
2858
        }
2859
        wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL);
2860
        if (wdest == NULL) {
2861
                g_free(wsrc);
2862
                return -1;
2863
        }
2864

    
2865
        if (keep_backup == FALSE) {
2866
                if (CopyFileW(wsrc, wdest, FALSE) == 0)
2867
                        err = TRUE;
2868
                g_free(wdest);
2869
                g_free(wsrc);
2870
                return err ? -1 : 0;
2871
        }
2872

    
2873
        if (is_file_exist(dest)) {
2874
                dest_bak = g_strconcat(dest, ".bak", NULL);
2875
                if (rename_force(dest, dest_bak) < 0) {
2876
                        FILE_OP_ERROR(dest, "rename");
2877
                        g_free(dest_bak);
2878
                        g_free(wdest);
2879
                        g_free(wsrc);
2880
                        return -1;
2881
                }
2882
        }
2883

    
2884
        if (CopyFileW(wsrc, wdest, FALSE) == 0)
2885
                err = TRUE;
2886

    
2887
        g_free(wdest);
2888
        g_free(wsrc);
2889
#else
2890
        gint srcfd, destfd;
2891
        gint n_read;
2892
        gchar buf[BUFFSIZE];
2893
        gchar *dest_bak = NULL;
2894
        gboolean err = FALSE;
2895

    
2896
        if ((srcfd = g_open(src, O_RDONLY, 0600)) < 0) {
2897
                FILE_OP_ERROR(src, "open");
2898
                return -1;
2899
        }
2900
        if (is_file_exist(dest)) {
2901
                dest_bak = g_strconcat(dest, ".bak", NULL);
2902
                if (rename_force(dest, dest_bak) < 0) {
2903
                        FILE_OP_ERROR(dest, "rename");
2904
                        close(srcfd);
2905
                        g_free(dest_bak);
2906
                        return -1;
2907
                }
2908
        }
2909

    
2910
        if ((destfd = g_open(dest, O_WRONLY | O_CREAT, 0600)) < 0) {
2911
                FILE_OP_ERROR(dest, "open");
2912
                close(srcfd);
2913
                if (dest_bak) {
2914
                        if (rename_force(dest_bak, dest) < 0)
2915
                                FILE_OP_ERROR(dest_bak, "rename");
2916
                        g_free(dest_bak);
2917
                }
2918
                return -1;
2919
        }
2920

    
2921
        while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) {
2922
                gchar *p = buf;
2923
                const gchar *endp = buf + n_read;
2924
                gint n_write;
2925

    
2926
                while (p < endp) {
2927
                        if ((n_write = write(destfd, p, endp - p)) < 0) {
2928
                                g_warning(_("writing to %s failed.\n"), dest);
2929
                                close(destfd);
2930
                                close(srcfd);
2931
                                g_unlink(dest);
2932
                                if (dest_bak) {
2933
                                        if (rename_force(dest_bak, dest) < 0)
2934
                                                FILE_OP_ERROR(dest_bak, "rename");
2935
                                        g_free(dest_bak);
2936
                                }
2937
                                return -1;
2938
                        }
2939
                        p += n_write;
2940
                }
2941
        }
2942

    
2943
        if (close(destfd) < 0) {
2944
                FILE_OP_ERROR(dest, "close");
2945
                err = TRUE;
2946
        }
2947
        close(srcfd);
2948
#endif
2949

    
2950
        if (err) {
2951
                g_unlink(dest);
2952
                if (dest_bak) {
2953
                        if (rename_force(dest_bak, dest) < 0)
2954
                                FILE_OP_ERROR(dest_bak, "rename");
2955
                        g_free(dest_bak);
2956
                }
2957
                return -1;
2958
        }
2959

    
2960
        if (keep_backup == FALSE && dest_bak)
2961
                g_unlink(dest_bak);
2962

    
2963
        g_free(dest_bak);
2964

    
2965
        return 0;
2966
}
2967

    
2968
gint copy_dir(const gchar *src, const gchar *dest)
2969
{
2970
        GDir *dir;
2971
        const gchar *dir_name;
2972
        gchar *src_file;
2973
        gchar *dest_file;
2974

    
2975
        if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
2976
                g_warning("failed to open directory: %s\n", src);
2977
                return -1;
2978
        }
2979

    
2980
        if (make_dir_hier(dest) < 0) {
2981
                g_dir_close(dir);
2982
                return -1;
2983
        }
2984

    
2985
        while ((dir_name = g_dir_read_name(dir)) != NULL) {
2986
                src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL);
2987
                dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name,
2988
                                        NULL);
2989
                if (is_file_exist(src_file))
2990
                        copy_file(src_file, dest_file, FALSE);
2991
                g_free(dest_file);
2992
                g_free(src_file);
2993
        }
2994

    
2995
        g_dir_close(dir);
2996

    
2997
        return 0;
2998
}
2999

    
3000
gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
3001
{
3002
        if (overwrite == FALSE && is_file_entry_exist(dest)) {
3003
                g_warning("move_file(): file %s already exists.", dest);
3004
                return -1;
3005
        }
3006

    
3007
        if (rename_force(src, dest) == 0) return 0;
3008

    
3009
        if (EXDEV != errno) {
3010
                FILE_OP_ERROR(src, "rename");
3011
                return -1;
3012
        }
3013

    
3014
        if (copy_file(src, dest, FALSE) < 0) return -1;
3015

    
3016
        g_unlink(src);
3017

    
3018
        return 0;
3019
}
3020

    
3021
gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
3022
{
3023
        gint n_read;
3024
        gint bytes_left, to_read;
3025
        gchar buf[BUFSIZ];
3026

    
3027
        g_return_val_if_fail(fp != NULL, -1);
3028
        g_return_val_if_fail(dest_fp != NULL, -1);
3029

    
3030
        if (fseek(fp, offset, SEEK_SET) < 0) {
3031
                perror("fseek");
3032
                return -1;
3033
        }
3034

    
3035
        bytes_left = length;
3036
        to_read = MIN(bytes_left, sizeof(buf));
3037

    
3038
        while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
3039
                if (n_read < to_read && ferror(fp))
3040
                        break;
3041
                if (fwrite(buf, n_read, 1, dest_fp) < 1) {
3042
                        g_warning("append_file_part: writing to file failed.\n");
3043
                        return -1;
3044
                }
3045
                bytes_left -= n_read;
3046
                if (bytes_left == 0)
3047
                        break;
3048
                to_read = MIN(bytes_left, sizeof(buf));
3049
        }
3050

    
3051
        if (ferror(fp)) {
3052
                perror("fread");
3053
                return -1;
3054
        }
3055
        if (fflush(dest_fp) == EOF) {
3056
                FILE_OP_ERROR("append_file_part", "fflush");
3057
                return -1;
3058
        }
3059

    
3060
        return 0;
3061
}
3062

    
3063
gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
3064
{
3065
        FILE *dest_fp;
3066

    
3067
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3068
                FILE_OP_ERROR(dest, "fopen");
3069
                return -1;
3070
        }
3071

    
3072
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3073
                FILE_OP_ERROR(dest, "chmod");
3074
                g_warning("can't change file mode\n");
3075
        }
3076

    
3077
        if (append_file_part(fp, offset, length, dest_fp) < 0) {
3078
                g_warning("writing to %s failed.\n", dest);
3079
                fclose(dest_fp);
3080
                g_unlink(dest);
3081
                return -1;
3082
        }
3083

    
3084
        if (fclose(dest_fp) == EOF) {
3085
                FILE_OP_ERROR(dest, "fclose");
3086
                g_unlink(dest);
3087
                return -1;
3088
        }
3089

    
3090
        return 0;
3091
}
3092

    
3093
/* convert line endings into CRLF. If the last line doesn't end with
3094
 * linebreak, add it.
3095
 */
3096
gchar *canonicalize_str(const gchar *str)
3097
{
3098
        const gchar *p;
3099
        guint new_len = 0;
3100
        gchar *out, *outp;
3101

    
3102
        for (p = str; *p != '\0'; ++p) {
3103
                if (*p != '\r') {
3104
                        ++new_len;
3105
                        if (*p == '\n')
3106
                                ++new_len;
3107
                }
3108
        }
3109
        if (p == str || *(p - 1) != '\n')
3110
                new_len += 2;
3111

    
3112
        out = outp = g_malloc(new_len + 1);
3113
        for (p = str; *p != '\0'; ++p) {
3114
                if (*p != '\r') {
3115
                        if (*p == '\n')
3116
                                *outp++ = '\r';
3117
                        *outp++ = *p;
3118
                }
3119
        }
3120
        if (p == str || *(p - 1) != '\n') {
3121
                *outp++ = '\r';
3122
                *outp++ = '\n';
3123
        }
3124
        *outp = '\0';
3125

    
3126
        return out;
3127
}
3128

    
3129
gint canonicalize_file(const gchar *src, const gchar *dest)
3130
{
3131
        FILE *src_fp, *dest_fp;
3132
        gchar buf[BUFFSIZE];
3133
        gint len;
3134
        gboolean err = FALSE;
3135
        gboolean last_linebreak = FALSE;
3136

    
3137
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3138
                FILE_OP_ERROR(src, "fopen");
3139
                return -1;
3140
        }
3141

    
3142
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3143
                FILE_OP_ERROR(dest, "fopen");
3144
                fclose(src_fp);
3145
                return -1;
3146
        }
3147

    
3148
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3149
                FILE_OP_ERROR(dest, "chmod");
3150
                g_warning("can't change file mode\n");
3151
        }
3152

    
3153
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3154
                gint r = 0;
3155

    
3156
                len = strlen(buf);
3157
                if (len == 0) break;
3158
                last_linebreak = FALSE;
3159

    
3160
                if (buf[len - 1] != '\n') {
3161
                        last_linebreak = TRUE;
3162
                        r = fputs(buf, dest_fp);
3163
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3164
                        r = fputs(buf, dest_fp);
3165
                } else {
3166
                        if (len > 1) {
3167
                                r = fwrite(buf, len - 1, 1, dest_fp);
3168
                                if (r != 1)
3169
                                        r = EOF;
3170
                        }
3171
                        if (r != EOF)
3172
                                r = fputs("\r\n", dest_fp);
3173
                }
3174

    
3175
                if (r == EOF) {
3176
                        g_warning("writing to %s failed.\n", dest);
3177
                        fclose(dest_fp);
3178
                        fclose(src_fp);
3179
                        g_unlink(dest);
3180
                        return -1;
3181
                }
3182
        }
3183

    
3184
        if (last_linebreak == TRUE) {
3185
                if (fputs("\r\n", dest_fp) == EOF)
3186
                        err = TRUE;
3187
        }
3188

    
3189
        if (ferror(src_fp)) {
3190
                FILE_OP_ERROR(src, "fgets");
3191
                err = TRUE;
3192
        }
3193
        fclose(src_fp);
3194
        if (fclose(dest_fp) == EOF) {
3195
                FILE_OP_ERROR(dest, "fclose");
3196
                err = TRUE;
3197
        }
3198

    
3199
        if (err) {
3200
                g_unlink(dest);
3201
                return -1;
3202
        }
3203

    
3204
        return 0;
3205
}
3206

    
3207
gint canonicalize_file_replace(const gchar *file)
3208
{
3209
        gchar *tmp_file;
3210

    
3211
        tmp_file = get_tmp_file();
3212

    
3213
        if (canonicalize_file(file, tmp_file) < 0) {
3214
                g_free(tmp_file);
3215
                return -1;
3216
        }
3217

    
3218
        if (move_file(tmp_file, file, TRUE) < 0) {
3219
                g_warning("can't replace %s .\n", file);
3220
                g_unlink(tmp_file);
3221
                g_free(tmp_file);
3222
                return -1;
3223
        }
3224

    
3225
        g_free(tmp_file);
3226
        return 0;
3227
}
3228

    
3229
FILE *canonicalize_file_stream(FILE *src_fp, gint *length)
3230
{
3231
        FILE *dest_fp;
3232
        gchar buf[BUFFSIZE];
3233
        gint len;
3234
        gint length_ = 0;
3235
        gboolean err = FALSE;
3236
        gboolean last_linebreak = FALSE;
3237

    
3238
        if ((dest_fp = my_tmpfile()) == NULL)
3239
                return NULL;
3240

    
3241
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3242
                gint r = 0;
3243

    
3244
                len = strlen(buf);
3245
                if (len == 0) break;
3246
                last_linebreak = FALSE;
3247

    
3248
                if (buf[len - 1] != '\n') {
3249
                        last_linebreak = TRUE;
3250
                        r = fputs(buf, dest_fp);
3251
                        length_ += len;
3252
                } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
3253
                        r = fputs(buf, dest_fp);
3254
                        length_ += len;
3255
                } else {
3256
                        if (len > 1) {
3257
                                r = fwrite(buf, len - 1, 1, dest_fp);
3258
                                if (r != 1)
3259
                                        r = EOF;
3260
                                else
3261
                                        length_ += len - 1;
3262
                        }
3263
                        if (r != EOF) {
3264
                                r = fputs("\r\n", dest_fp);
3265
                                length_ += 2;
3266
                        }
3267
                }
3268

    
3269
                if (r == EOF) {
3270
                        g_warning("writing to temporary file failed.\n");
3271
                        fclose(dest_fp);
3272
                        return NULL;
3273
                }
3274
        }
3275

    
3276
        if (last_linebreak == TRUE) {
3277
                if (fputs("\r\n", dest_fp) == EOF)
3278
                        err = TRUE;
3279
                else
3280
                        length_ += 2;
3281
        }
3282

    
3283
        if (ferror(src_fp)) {
3284
                FILE_OP_ERROR("canonicalize_file_stream", "fgets");
3285
                err = TRUE;
3286
        }
3287
        if (fflush(dest_fp) == EOF) {
3288
                FILE_OP_ERROR("canonicalize_file_stream", "fflush");
3289
                err = TRUE;
3290
        }
3291

    
3292
        if (err) {
3293
                fclose(dest_fp);
3294
                return NULL;
3295
        }
3296

    
3297
        if (length)
3298
                *length = length_;
3299

    
3300
        rewind(dest_fp);
3301
        return dest_fp;
3302
}
3303

    
3304
gint uncanonicalize_file(const gchar *src, const gchar *dest)
3305
{
3306
        FILE *src_fp, *dest_fp;
3307
        gchar buf[BUFFSIZE];
3308
        gboolean err = FALSE;
3309

    
3310
        if ((src_fp = g_fopen(src, "rb")) == NULL) {
3311
                FILE_OP_ERROR(src, "fopen");
3312
                return -1;
3313
        }
3314

    
3315
        if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
3316
                FILE_OP_ERROR(dest, "fopen");
3317
                fclose(src_fp);
3318
                return -1;
3319
        }
3320

    
3321
        if (change_file_mode_rw(dest_fp, dest) < 0) {
3322
                FILE_OP_ERROR(dest, "chmod");
3323
                g_warning("can't change file mode\n");
3324
        }
3325

    
3326
        while (fgets(buf, sizeof(buf), src_fp) != NULL) {
3327
                strcrchomp(buf);
3328
                if (fputs(buf, dest_fp) == EOF) {
3329
                        g_warning("writing to %s failed.\n", dest);
3330
                        fclose(dest_fp);
3331
                        fclose(src_fp);
3332
                        g_unlink(dest);
3333
                        return -1;
3334
                }
3335
        }
3336

    
3337
        if (ferror(src_fp)) {
3338
                FILE_OP_ERROR(src, "fgets");
3339
                err = TRUE;
3340
        }
3341
        fclose(src_fp);
3342
        if (fclose(dest_fp) == EOF) {
3343
                FILE_OP_ERROR(dest, "fclose");
3344
                err = TRUE;
3345
        }
3346

    
3347
        if (err) {
3348
                g_unlink(dest);
3349
                return -1;
3350
        }
3351

    
3352
        return 0;
3353
}
3354

    
3355
gint uncanonicalize_file_replace(const gchar *file)
3356
{
3357
        gchar *tmp_file;
3358

    
3359
        tmp_file = get_tmp_file();
3360

    
3361
        if (uncanonicalize_file(file, tmp_file) < 0) {
3362
                g_free(tmp_file);
3363
                return -1;
3364
        }
3365

    
3366
        if (move_file(tmp_file, file, TRUE) < 0) {
3367
                g_warning("can't replace %s .\n", file);
3368
                g_unlink(tmp_file);
3369
                g_free(tmp_file);
3370
                return -1;
3371
        }
3372

    
3373
        g_free(tmp_file);
3374
        return 0;
3375
}
3376

    
3377
gchar *normalize_newlines(const gchar *str)
3378
{
3379
        const gchar *p = str;
3380
        gchar *out, *outp;
3381

    
3382
        out = outp = g_malloc(strlen(str) + 1);
3383
        for (p = str; *p != '\0'; ++p) {
3384
                if (*p == '\r') {
3385
                        if (*(p + 1) != '\n')
3386
                                *outp++ = '\n';
3387
                } else
3388
                        *outp++ = *p;
3389
        }
3390

    
3391
        *outp = '\0';
3392

    
3393
        return out;
3394
}
3395

    
3396
gchar *strchomp_all(const gchar *str)
3397
{
3398
        const gchar *p = str;
3399
        const gchar *newline, *last;
3400
        gchar *out, *outp;
3401

    
3402
        out = outp = g_malloc(strlen(str) + 1);
3403
        while (*p != '\0') {
3404
                newline = strchr(p, '\n');
3405
                if (newline) {
3406
                        for (last = newline;
3407
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3408
                                ;
3409
                        strncpy(outp, p, last - p);
3410
                        outp += last - p;
3411

    
3412
                        if (p < newline && *(newline - 1) == '\r') {
3413
                                strncpy(outp, newline - 1, 2);
3414
                                outp += 2;
3415
                        } else {
3416
                                *outp++ = *newline;
3417
                        }
3418

    
3419
                        p = newline + 1;
3420
                } else {
3421
                        for (last = p + strlen(p);
3422
                             p < last && g_ascii_isspace(*(last - 1)); --last)
3423
                                ;
3424
                        strncpy(outp, p, last - p);
3425
                        outp += last - p;
3426
                        break;
3427
                }
3428
        }
3429

    
3430
        *outp = '\0';
3431

    
3432
        return out;
3433
}
3434

    
3435
FILE *get_outgoing_rfc2822_file(FILE *fp)
3436
{
3437
        gchar buf[BUFFSIZE];
3438
        FILE *outfp;
3439

    
3440
        outfp = my_tmpfile();
3441
        if (!outfp) {
3442
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile");
3443
                return NULL;
3444
        }
3445

    
3446
        /* output header part */
3447
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3448
                strretchomp(buf);
3449
                if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
3450
                        gint next;
3451

    
3452
                        for (;;) {
3453
                                next = fgetc(fp);
3454
                                if (next == EOF)
3455
                                        break;
3456
                                else if (next != ' ' && next != '\t') {
3457
                                        ungetc(next, fp);
3458
                                        break;
3459
                                }
3460
                                if (fgets(buf, sizeof(buf), fp) == NULL)
3461
                                        break;
3462
                        }
3463
                } else {
3464
                        if (fputs(buf, outfp) == EOF)
3465
                                goto file_error;
3466
                        if (fputs("\r\n", outfp) == EOF)
3467
                                goto file_error;
3468
                        if (buf[0] == '\0')
3469
                                break;
3470
                }
3471
        }
3472

    
3473
        /* output body part */
3474
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3475
                strretchomp(buf);
3476
                if (buf[0] == '.') {
3477
                        if (fputc('.', outfp) == EOF)
3478
                                goto file_error;
3479
                }
3480
                if (fputs(buf, outfp) == EOF)
3481
                        goto file_error;
3482
                if (fputs("\r\n", outfp) == EOF)
3483
                        goto file_error;
3484
        }
3485

    
3486
        if (fflush(outfp) == EOF) {
3487
                FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush");
3488
                goto file_error;
3489
        }
3490

    
3491
        rewind(outfp);
3492
        return outfp;
3493

    
3494
file_error:
3495
        g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n");
3496
        fclose(outfp);
3497
        return NULL;
3498
}
3499

    
3500
gchar *get_outgoing_rfc2822_str(FILE *fp)
3501
{
3502
        gchar buf[BUFFSIZE];
3503
        GString *str;
3504
        gchar *ret;
3505

    
3506
        str = g_string_new(NULL);
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
#if 0
3526
                } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) {
3527
                        get_rfc822_date(buf, sizeof(buf));
3528
                        g_string_append_printf(str, "Date: %s\r\n", buf);
3529
#endif
3530
                } else {
3531
                        g_string_append(str, buf);
3532
                        g_string_append(str, "\r\n");
3533
                        if (buf[0] == '\0')
3534
                                break;
3535
                }
3536
        }
3537

    
3538
        /* output body part */
3539
        while (fgets(buf, sizeof(buf), fp) != NULL) {
3540
                strretchomp(buf);
3541
                if (buf[0] == '.')
3542
                        g_string_append_c(str, '.');
3543
                g_string_append(str, buf);
3544
                g_string_append(str, "\r\n");
3545
        }
3546

    
3547
        ret = str->str;
3548
        g_string_free(str, FALSE);
3549

    
3550
        return ret;
3551
}
3552

    
3553
/*
3554
 * Create a new boundary in a way that it is very unlikely that this
3555
 * will occur in the following text.  It would be easy to ensure
3556
 * uniqueness if everything is either quoted-printable or base64
3557
 * encoded (note that conversion is allowed), but because MIME bodies
3558
 * may be nested, it may happen that the same boundary has already
3559
 * been used. We avoid scanning the message for conflicts and hope the
3560
 * best.
3561
 *
3562
 *   boundary := 0*69<bchars> bcharsnospace
3563
 *   bchars := bcharsnospace / " "
3564
 *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
3565
 *                    "+" / "_" / "," / "-" / "." /
3566
 *                    "/" / ":" / "=" / "?"
3567
 *
3568
 * some special characters removed because of buggy MTAs
3569
 */
3570

    
3571
gchar *generate_mime_boundary(const gchar *prefix)
3572
{
3573
        static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3574
                             "abcdefghijklmnopqrstuvwxyz"
3575
                             "1234567890+_./=";
3576
        gchar buf_uniq[17];
3577
        gchar buf_date[64];
3578
        gint i;
3579

    
3580
        for (i = 0; i < sizeof(buf_uniq) - 1; i++)
3581
                buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
3582
        buf_uniq[i] = '\0';
3583

    
3584
        get_rfc822_date(buf_date, sizeof(buf_date));
3585
        subst_chars(buf_date, " ,:", '_');
3586

    
3587
        return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart",
3588
                               buf_date, buf_uniq);
3589
}
3590

    
3591
gint change_file_mode_rw(FILE *fp, const gchar *file)
3592
{
3593
#ifdef G_OS_WIN32
3594
        DWORD attr;
3595
        BOOL retval;
3596

    
3597
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3598
                wchar_t *wpath;
3599

    
3600
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
3601
                if (wpath == NULL)
3602
                        return -1;
3603

    
3604
                attr = GetFileAttributesW(wpath);
3605
                retval = SetFileAttributesW
3606
                        (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3607

    
3608
                g_free(wpath);
3609
        } else {
3610
                gchar *cp_path;
3611

    
3612
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
3613
                if (cp_path == NULL)
3614
                        return -1;
3615

    
3616
                attr = GetFileAttributesA(cp_path);
3617
                retval = SetFileAttributesA
3618
                        (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN));
3619

    
3620
                g_free(cp_path);
3621
        }
3622

    
3623
        if (retval)
3624
                return 0;
3625
        else
3626
                return -1;
3627
#else
3628
#if HAVE_FCHMOD
3629
        if (fp)
3630
                return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
3631
        else
3632
#endif
3633
                return g_chmod(file, S_IRUSR|S_IWUSR);
3634
#endif
3635
}
3636

    
3637
#ifdef G_OS_WIN32
3638
gchar *_s_tempnam(const gchar *dir, const gchar *prefix)
3639
{
3640
        if (G_WIN32_HAVE_WIDECHAR_API()) {
3641
                wchar_t *wpath;
3642
                wchar_t *wprefix;
3643
                wchar_t *wname;
3644
                gint save_errno;
3645
                gchar *name;
3646

    
3647
                wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL);
3648
                if (wpath == NULL) {
3649
                        errno = EINVAL;
3650
                        return NULL;
3651
                }
3652
                wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL);
3653
                if (wprefix == NULL) {
3654
                        errno = EINVAL;
3655
                        g_free(wpath);
3656
                        return NULL;
3657
                }
3658

    
3659
                wname = _wtempnam(wpath, wprefix);
3660
                save_errno = errno;
3661

    
3662
                name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL);
3663
                if (name == NULL) {
3664
                        save_errno = EINVAL;
3665
                }
3666

    
3667
                g_free(wname);
3668
                g_free(wprefix);
3669
                g_free(wpath);
3670

    
3671
                errno = save_errno;
3672
                return name;
3673
        } else {
3674
                gchar *cp_path;
3675
                gchar *cp_prefix;
3676
                gchar *cp_name;
3677
                gint save_errno;
3678
                gchar *name;
3679

    
3680
                cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL);
3681
                if (cp_path == NULL) {
3682
                        errno = EINVAL;
3683
                        return NULL;
3684
                }
3685

    
3686
                cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL);
3687
                if (cp_prefix == NULL) {
3688
                        errno = EINVAL;
3689
                        g_free(cp_path);
3690
                        return NULL;
3691
                }
3692

    
3693
                cp_name = _tempnam(cp_path, cp_prefix);
3694
                save_errno = errno;
3695

    
3696
                name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL);
3697
                if (name == NULL) {
3698
                        save_errno = EINVAL;
3699
                }
3700

    
3701
                g_free(cp_name);
3702
                g_free(cp_prefix);
3703
                g_free(cp_path);
3704

    
3705
                errno = save_errno;
3706
                return name;
3707
        }
3708
}
3709
#endif
3710

    
3711
FILE *my_tmpfile(void)
3712
{
3713
#ifdef G_OS_WIN32
3714
        const gchar *tmpdir;
3715
        gchar *fname;
3716
        gint fd;
3717
        FILE *fp;
3718

    
3719
        tmpdir = get_tmp_dir();
3720
        fname = _s_tempnam(tmpdir, "sylph");
3721
        if (!fname)
3722
                return NULL;
3723

    
3724
        fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL |
3725
                    _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600);
3726
        if (fd < 0) {
3727
                g_free(fname);
3728
                return NULL;
3729
        }
3730

    
3731
        fp = fdopen(fd, "w+b");
3732
        if (!fp) {
3733
                perror("fdopen");
3734
                close(fd);
3735
        }
3736

    
3737
        g_free(fname);
3738

    
3739
        return fp;
3740
#else
3741
        const gchar suffix[] = ".XXXXXX";
3742
        const gchar *tmpdir;
3743
        guint tmplen;
3744
        const gchar *progname;
3745
        guint proglen;
3746
        gchar *fname;
3747
        gint fd;
3748
        FILE *fp;
3749

    
3750
        tmpdir = get_tmp_dir();
3751
        tmplen = strlen(tmpdir);
3752
        progname = g_get_prgname();
3753
        if (!progname)
3754
                progname = "sylph";
3755
        proglen = strlen(progname);
3756
        fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix));
3757

    
3758
        memcpy(fname, tmpdir, tmplen);
3759
        fname[tmplen] = G_DIR_SEPARATOR;
3760
        memcpy(fname + tmplen + 1, progname, proglen);
3761
        memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3762

    
3763
        fd = g_mkstemp(fname);
3764
        if (fd < 0) {
3765
                g_free(fname);
3766
                return tmpfile();
3767
        }
3768

    
3769
        g_unlink(fname);
3770

    
3771
        fp = fdopen(fd, "w+b");
3772
        if (!fp) {
3773
                perror("fdopen");
3774
                close(fd);
3775
        }
3776

    
3777
        g_free(fname);
3778

    
3779
        return fp;
3780
#endif
3781
}
3782

    
3783
FILE *str_open_as_stream(const gchar *str)
3784
{
3785
        FILE *fp;
3786
        size_t len;
3787

    
3788
        g_return_val_if_fail(str != NULL, NULL);
3789

    
3790
        fp = my_tmpfile();
3791
        if (!fp) {
3792
                FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3793
                return NULL;
3794
        }
3795

    
3796
        len = strlen(str);
3797
        if (len == 0) return fp;
3798

    
3799
        if (fwrite(str, len, 1, fp) != 1) {
3800
                FILE_OP_ERROR("str_open_as_stream", "fwrite");
3801
                fclose(fp);
3802
                return NULL;
3803
        }
3804
        if (fflush(fp) == EOF) {
3805
                FILE_OP_ERROR("str_open_as_stream", "fflush");
3806
                fclose(fp);
3807
                return NULL;
3808
        }
3809

    
3810
        rewind(fp);
3811
        return fp;
3812
}
3813

    
3814
gint str_write_to_file(const gchar *str, const gchar *file)
3815
{
3816
        FILE *fp;
3817
        size_t len;
3818

    
3819
        g_return_val_if_fail(str != NULL, -1);
3820
        g_return_val_if_fail(file != NULL, -1);
3821

    
3822
        if ((fp = g_fopen(file, "wb")) == NULL) {
3823
                FILE_OP_ERROR(file, "fopen");
3824
                return -1;
3825
        }
3826

    
3827
        len = strlen(str);
3828
        if (len == 0) {
3829
                fclose(fp);
3830
                return 0;
3831
        }
3832

    
3833
        if (fwrite(str, len, 1, fp) != 1) {
3834
                FILE_OP_ERROR(file, "fwrite");
3835
                fclose(fp);
3836
                g_unlink(file);
3837
                return -1;
3838
        }
3839

    
3840
        if (fclose(fp) == EOF) {
3841
                FILE_OP_ERROR(file, "fclose");
3842
                g_unlink(file);
3843
                return -1;
3844
        }
3845

    
3846
        return 0;
3847
}
3848

    
3849
gchar *file_read_to_str(const gchar *file)
3850
{
3851
        FILE *fp;
3852
        gchar *str;
3853

    
3854
        g_return_val_if_fail(file != NULL, NULL);
3855

    
3856
        if ((fp = g_fopen(file, "rb")) == NULL) {
3857
                FILE_OP_ERROR(file, "fopen");
3858
                return NULL;
3859
        }
3860

    
3861
        str = file_read_stream_to_str(fp);
3862

    
3863
        fclose(fp);
3864

    
3865
        return str;
3866
}
3867

    
3868
gchar *file_read_stream_to_str(FILE *fp)
3869
{
3870
        GByteArray *array;
3871
        guchar buf[BUFSIZ];
3872
        gint n_read;
3873
        gchar *str;
3874

    
3875
        g_return_val_if_fail(fp != NULL, NULL);
3876

    
3877
        array = g_byte_array_new();
3878

    
3879
        while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3880
                if (n_read < sizeof(buf) && ferror(fp))
3881
                        break;
3882
                g_byte_array_append(array, buf, n_read);
3883
        }
3884

    
3885
        if (ferror(fp)) {
3886
                FILE_OP_ERROR("file stream", "fread");
3887
                g_byte_array_free(array, TRUE);
3888
                return NULL;
3889
        }
3890

    
3891
        buf[0] = '\0';
3892
        g_byte_array_append(array, buf, 1);
3893
        str = (gchar *)array->data;
3894
        g_byte_array_free(array, FALSE);
3895

    
3896
        return str;
3897
}
3898

    
3899
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3900
static gchar **argv_utf8_to_locale(gchar **argv)
3901
{
3902
        gint argc = 0, i;
3903
        gchar **cp_argv;
3904

    
3905
        while (argv[argc] != NULL)
3906
                argc++;
3907

    
3908
        cp_argv = g_new(gchar *, argc + 1);
3909

    
3910
        for (i = 0; i < argc; i++) {
3911
                cp_argv[i] = g_locale_from_utf8(argv[i], -1, NULL, NULL, NULL);
3912
                if (cp_argv[i] == NULL) {
3913
                        g_warning("Failed to convert from UTF-8 to locale encoding: %s\n", argv[i]);
3914
                        g_strfreev(cp_argv);
3915
                        return NULL;
3916
                }
3917
        }
3918
        cp_argv[i] = NULL;
3919

    
3920
        return cp_argv;
3921
}
3922
#endif
3923

    
3924
gint execute_async(gchar *const argv[])
3925
{
3926
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3927
        gchar **cp_argv;
3928

    
3929
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3930

    
3931
        cp_argv = argv_utf8_to_locale((gchar **)argv);
3932
        if (!cp_argv)
3933
                return -1;
3934
        if (g_spawn_async(NULL, cp_argv, NULL, G_SPAWN_SEARCH_PATH,
3935
                          NULL, NULL, NULL, NULL) == FALSE) {
3936
                g_warning("Can't execute command: %s\n", argv[0]);
3937
                g_strfreev(cp_argv);
3938
                return -1;
3939
        }
3940
        g_strfreev(cp_argv);
3941
#else
3942
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3943

    
3944
        if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3945
                          NULL, NULL, NULL, NULL) == FALSE) {
3946
                g_warning("Can't execute command: %s\n", argv[0]);
3947
                return -1;
3948
        }
3949
#endif
3950

    
3951
        return 0;
3952
}
3953

    
3954
gint execute_sync(gchar *const argv[])
3955
{
3956
        gint status;
3957
#if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2)
3958
        gchar **cp_argv;
3959
#endif
3960

    
3961
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3962

    
3963
#ifdef G_OS_WIN32
3964
#if !GLIB_CHECK_VERSION(2, 8, 2)
3965
        cp_argv = argv_utf8_to_locale((gchar **)argv);
3966
        if (!cp_argv)
3967
                return -1;
3968
        if (g_spawn_sync(NULL, cp_argv, NULL,
3969
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
3970
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3971
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3972
                g_warning("Can't execute command: %s\n", argv[0]);
3973
                g_strfreev(cp_argv);
3974
                return -1;
3975
        }
3976
        g_strfreev(cp_argv);
3977
#else /* !GLIB_CHECK_VERSION */
3978
        if (g_spawn_sync(NULL, (gchar **)argv, NULL,
3979
                         G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN |
3980
                         G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3981
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3982
                g_warning("Can't execute command: %s\n", argv[0]);
3983
                return -1;
3984
        }
3985
#endif /* !GLIB_CHECK_VERSION */
3986

    
3987
        return status;
3988
#else /* G_OS_WIN32 */
3989
        if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3990
                         NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3991
                g_warning("Can't execute command: %s\n", argv[0]);
3992
                return -1;
3993
        }
3994

    
3995
        if (WIFEXITED(status))
3996
                return WEXITSTATUS(status);
3997
        else
3998
                return -1;
3999
#endif /* G_OS_WIN32 */
4000
}
4001

    
4002
gint execute_command_line(const gchar *cmdline, gboolean async)
4003
{
4004
        gchar **argv;
4005
        gint ret;
4006

    
4007
        if (debug_mode) {
4008
                gchar *utf8_cmdline;
4009

    
4010
                utf8_cmdline = g_filename_to_utf8
4011
                        (cmdline, -1, NULL, NULL, NULL);
4012
                debug_print("execute_command_line(): executing: %s\n",
4013
                            utf8_cmdline ? utf8_cmdline : cmdline);
4014
                g_free(utf8_cmdline);
4015
        }
4016

    
4017
        argv = strsplit_with_quote(cmdline, " ", 0);
4018

    
4019
        if (async)
4020
                ret = execute_async(argv);
4021
        else
4022
                ret = execute_sync(argv);
4023

    
4024
        g_strfreev(argv);
4025

    
4026
        return ret;
4027
}
4028

    
4029
#if USE_THREADS
4030
typedef struct _CmdData
4031
{
4032
        const gchar *cmdline;
4033
        volatile gint flag;
4034
        gint status;
4035
} CmdData;
4036

    
4037
static gpointer execute_command_line_async_func(gpointer data)
4038
{
4039
        CmdData *cmd_data = (CmdData *)data;
4040
        gchar **argv;
4041

    
4042
        argv = strsplit_with_quote(cmd_data->cmdline, " ", 0);
4043
        cmd_data->status = execute_sync(argv);
4044
        g_strfreev(argv);
4045

    
4046
        debug_print("execute_command_line_async_func: exec done: %s\n",
4047
                    cmd_data->cmdline);
4048
        g_atomic_int_set(&cmd_data->flag, 1);
4049
        g_main_context_wakeup(NULL);
4050

    
4051
        return GINT_TO_POINTER(0);
4052
}
4053

    
4054
gint execute_command_line_async_wait(const gchar *cmdline)
4055
{
4056
        CmdData data = {NULL, 0, 0};
4057
        GThread *thread;
4058

    
4059
        if (debug_mode) {
4060
                gchar *utf8_cmdline;
4061

    
4062
                utf8_cmdline = g_filename_to_utf8
4063
                        (cmdline, -1, NULL, NULL, NULL);
4064
                debug_print("execute_command_line(): executing: %s\n",
4065
                            utf8_cmdline ? utf8_cmdline : cmdline);
4066
                g_free(utf8_cmdline);
4067
        }
4068

    
4069
        data.cmdline = cmdline;
4070
        thread = g_thread_create(execute_command_line_async_func, &data, TRUE,
4071
                                 NULL);
4072
        if (!thread)
4073
                return -1;
4074

    
4075
        debug_print("execute_command_line_async_wait: waiting thread\n");
4076
        while (g_atomic_int_get(&data.flag) == 0)
4077
                event_loop_iterate();
4078

    
4079
        g_thread_join(thread);
4080
        debug_print("execute_command_line_async_wait: thread exited\n");
4081

    
4082
        return data.status;
4083
}
4084
#else /* USE_THREADS */
4085
gint execute_command_line_async_wait(const gchar *cmdline)
4086
{
4087
        return execute_command_line(cmdline, FALSE);
4088
}
4089
#endif /* USE_THREADS */
4090

    
4091
gint execute_open_file(const gchar *file, const gchar *content_type)
4092
{
4093
#ifdef G_OS_WIN32
4094
        g_return_val_if_fail(file != NULL, -1);
4095

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

    
4098
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4099
                wchar_t *wpath;
4100

    
4101
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4102
                if (wpath == NULL)
4103
                        return -1;
4104

    
4105
                ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL);
4106

    
4107
                g_free(wpath);
4108

    
4109
                return 0;
4110
        } else {
4111
                gchar *cp_path;
4112

    
4113
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4114
                if (cp_path == NULL)
4115
                        return -1;
4116

    
4117
                ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL);
4118

    
4119
                g_free(cp_path);
4120

    
4121
                return 0;
4122
        }
4123
#elif defined(__APPLE__)
4124
        const gchar *argv[3] = {"open", NULL, NULL};
4125

    
4126
        g_return_val_if_fail(file != NULL, -1);
4127

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

    
4130
        argv[1] = file;
4131
        execute_async(argv);
4132
#endif
4133

    
4134
        return 0;
4135
}
4136

    
4137
gint execute_print_file(const gchar *file)
4138
{
4139
        g_return_val_if_fail(file != NULL, -1);
4140

    
4141
#ifdef G_OS_WIN32
4142
        log_print("printing %s\n", file);
4143

    
4144
        if (G_WIN32_HAVE_WIDECHAR_API()) {
4145
                wchar_t *wpath;
4146

    
4147
                wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL);
4148
                if (wpath == NULL)
4149
                        return -1;
4150

    
4151
                ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL);
4152

    
4153
                g_free(wpath);
4154

    
4155
                return 0;
4156
        } else {
4157
                gchar *cp_path;
4158

    
4159
                cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL);
4160
                if (cp_path == NULL)
4161
                        return -1;
4162

    
4163
                ShellExecuteA(NULL, "print", cp_path, NULL, NULL,
4164
                              SW_SHOWNORMAL);
4165

    
4166
                g_free(cp_path);
4167

    
4168
                return 0;
4169
        }
4170
#endif
4171
        return 0;
4172
}
4173

    
4174
gchar *get_command_output(const gchar *cmdline)
4175
{
4176
        gchar *child_stdout;
4177
        gint status;
4178

    
4179
        g_return_val_if_fail(cmdline != NULL, NULL);
4180

    
4181
        debug_print("get_command_output(): executing: %s\n", cmdline);
4182

    
4183
        if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
4184
                                      NULL) == FALSE) {
4185
                g_warning("Can't execute command: %s\n", cmdline);
4186
                return NULL;
4187
        }
4188

    
4189
        return child_stdout;
4190
}
4191

    
4192
gint open_uri(const gchar *uri, const gchar *cmdline)
4193
{
4194
        gchar buf[BUFFSIZE];
4195

    
4196
        g_return_val_if_fail(uri != NULL, -1);
4197

    
4198
#if defined(G_OS_WIN32) || defined(__APPLE__)
4199
        if (!cmdline || cmdline[0] == '\0')
4200
                return execute_open_file(uri, NULL);
4201
#endif
4202

    
4203
        if (cmdline && str_find_format_times(cmdline, 's') == 1)
4204
                g_snprintf(buf, sizeof(buf), cmdline, uri);
4205
        else {
4206
                if (cmdline)
4207
                        g_warning("Open URI command line is invalid "
4208
                                  "(there must be only one '%%s'): %s",
4209
                                  cmdline);
4210
                g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri);
4211
        }
4212

    
4213
        execute_command_line(buf, TRUE);
4214

    
4215
        return 0;
4216
}
4217

    
4218
time_t remote_tzoffset_sec(const gchar *zone)
4219
{
4220
        static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
4221
        gchar zone3[4];
4222
        gchar *p;
4223
        gchar c;
4224
        gint iustz;
4225
        gint offset;
4226
        time_t remoteoffset;
4227

    
4228
        strncpy(zone3, zone, 3);
4229
        zone3[3] = '\0';
4230
        remoteoffset = 0;
4231

    
4232
        if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
4233
            (c == '+' || c == '-')) {
4234
                remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
4235
                if (c == '-')
4236
                        remoteoffset = -remoteoffset;
4237
        } else if (!strncmp(zone, "UT" , 2) ||
4238
                   !strncmp(zone, "GMT", 2)) {
4239
                remoteoffset = 0;
4240
        } else if (strlen(zone3) == 3) {
4241
                for (p = ustzstr; *p != '\0'; p += 3) {
4242
                        if (!g_ascii_strncasecmp(p, zone3, 3)) {
4243
                                iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
4244
                                remoteoffset = iustz * 3600;
4245
                                break;
4246
                        }
4247
                }
4248
                if (*p == '\0')
4249
                        return -1;
4250
        } else if (strlen(zone3) == 1) {
4251
                switch (zone[0]) {
4252
                case 'Z': remoteoffset =   0; break;
4253
                case 'A': remoteoffset =  -1; break;
4254
                case 'B': remoteoffset =  -2; break;
4255
                case 'C': remoteoffset =  -3; break;
4256
                case 'D': remoteoffset =  -4; break;
4257
                case 'E': remoteoffset =  -5; break;
4258
                case 'F': remoteoffset =  -6; break;
4259
                case 'G': remoteoffset =  -7; break;
4260
                case 'H': remoteoffset =  -8; break;
4261
                case 'I': remoteoffset =  -9; break;
4262
                case 'K': remoteoffset = -10; break; /* J is not used */
4263
                case 'L': remoteoffset = -11; break;
4264
                case 'M': remoteoffset = -12; break;
4265
                case 'N': remoteoffset =   1; break;
4266
                case 'O': remoteoffset =   2; break;
4267
                case 'P': remoteoffset =   3; break;
4268
                case 'Q': remoteoffset =   4; break;
4269
                case 'R': remoteoffset =   5; break;
4270
                case 'S': remoteoffset =   6; break;
4271
                case 'T': remoteoffset =   7; break;
4272
                case 'U': remoteoffset =   8; break;
4273
                case 'V': remoteoffset =   9; break;
4274
                case 'W': remoteoffset =  10; break;
4275
                case 'X': remoteoffset =  11; break;
4276
                case 'Y': remoteoffset =  12; break;
4277
                default:  remoteoffset =   0; break;
4278
                }
4279
                remoteoffset = remoteoffset * 3600;
4280
        } else
4281
                return -1;
4282

    
4283
        return remoteoffset;
4284
}
4285

    
4286
time_t tzoffset_sec(time_t *now)
4287
{
4288
        struct tm gmt, *tmp, *lt;
4289
        gint off;
4290

    
4291
        tmp = gmtime(now);
4292
        g_return_val_if_fail(tmp != NULL, -1);
4293
        gmt = *tmp;
4294
        lt = localtime(now);
4295
        g_return_val_if_fail(lt != NULL, -1);
4296

    
4297
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4298

    
4299
        if (lt->tm_year < gmt.tm_year)
4300
                off -= 24 * 60;
4301
        else if (lt->tm_year > gmt.tm_year)
4302
                off += 24 * 60;
4303
        else if (lt->tm_yday < gmt.tm_yday)
4304
                off -= 24 * 60;
4305
        else if (lt->tm_yday > gmt.tm_yday)
4306
                off += 24 * 60;
4307

    
4308
        if (off >= 24 * 60)                /* should be impossible */
4309
                off = 23 * 60 + 59;        /* if not, insert silly value */
4310
        if (off <= -24 * 60)
4311
                off = -(23 * 60 + 59);
4312

    
4313
        return off * 60;
4314
}
4315

    
4316
/* calculate timezone offset (buf must not be less than 6 bytes) */
4317
gchar *tzoffset_buf(gchar *buf, time_t *now)
4318
{
4319
        struct tm gmt, *tmp, *lt;
4320
        gint off;
4321
        gchar sign = '+';
4322

    
4323
        tmp = gmtime(now);
4324
        g_return_val_if_fail(tmp != NULL, NULL);
4325
        gmt = *tmp;
4326
        lt = localtime(now);
4327
        g_return_val_if_fail(lt != NULL, NULL);
4328

    
4329
        off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
4330

    
4331
        if (lt->tm_year < gmt.tm_year)
4332
                off -= 24 * 60;
4333
        else if (lt->tm_year > gmt.tm_year)
4334
                off += 24 * 60;
4335
        else if (lt->tm_yday < gmt.tm_yday)
4336
                off -= 24 * 60;
4337
        else if (lt->tm_yday > gmt.tm_yday)
4338
                off += 24 * 60;
4339

    
4340
        if (off < 0) {
4341
                sign = '-';
4342
                off = -off;
4343
        }
4344

    
4345
        if (off >= 24 * 60)                /* should be impossible */
4346
                off = 23 * 60 + 59;        /* if not, insert silly value */
4347

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

    
4350
        return buf;
4351
}
4352

    
4353
gchar *tzoffset(time_t *now)
4354
{
4355
        static gchar offset_string[6];
4356

    
4357
        return tzoffset_buf(offset_string, now);
4358
}
4359

    
4360
void get_rfc822_date(gchar *buf, gint len)
4361
{
4362
        struct tm *lt;
4363
        time_t t;
4364
        gchar day[4], mon[4];
4365
        gint dd, hh, mm, ss, yyyy;
4366
        gchar off[6];
4367

    
4368
        t = time(NULL);
4369
        lt = localtime(&t);
4370

    
4371
        sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
4372
               day, mon, &dd, &hh, &mm, &ss, &yyyy);
4373
        g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
4374
                   day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t));
4375
}
4376

    
4377
/* just a wrapper to suppress the warning of gcc about %c */
4378
size_t my_strftime(gchar *s, size_t max, const gchar *format,
4379
                   const struct tm *tm)
4380
{
4381
        return strftime(s, max, format, tm);
4382
}
4383

    
4384
/* UI hints */
4385

    
4386
static UIUpdateFunc ui_update_func = NULL;
4387

    
4388
void set_ui_update_func(UIUpdateFunc func)
4389
{
4390
        ui_update_func = func;
4391
}
4392

    
4393
void ui_update(void)
4394
{
4395
        if (ui_update_func)
4396
                ui_update_func();
4397
}
4398

    
4399
static EventLoopFunc event_loop_func = NULL;
4400

    
4401
void set_event_loop_func(EventLoopFunc func)
4402
{
4403
        event_loop_func = func;
4404
}
4405

    
4406
void event_loop_iterate(void)
4407
{
4408
        if (event_loop_func)
4409
                event_loop_func();
4410
        else
4411
                g_main_context_iteration(NULL, TRUE);
4412
}
4413

    
4414
static ProgressFunc progress_func = NULL;
4415

    
4416
void set_progress_func(ProgressFunc func)
4417
{
4418
        progress_func = func;
4419
}
4420

    
4421
void progress_show(gint cur, gint total)
4422
{
4423
        if (progress_func)
4424
                progress_func(cur, total);
4425
}
4426

    
4427
/* user input */
4428

    
4429
static QueryPasswordFunc query_password_func = NULL;
4430

    
4431
void set_input_query_password_func(QueryPasswordFunc func)
4432
{
4433
        query_password_func = func;
4434
}
4435

    
4436
gchar *input_query_password(const gchar *server, const gchar *user)
4437
{
4438
        if (query_password_func)
4439
                return query_password_func(server, user);
4440
        else
4441
                return NULL;
4442
}
4443

    
4444
/* logging */
4445

    
4446
static FILE *log_fp = NULL;
4447
#if USE_THREADS
4448
G_LOCK_DEFINE_STATIC(log_fp);
4449
#define S_LOCK(name)        G_LOCK(name)
4450
#define S_UNLOCK(name)        G_UNLOCK(name)
4451
#else
4452
#define S_LOCK(name)
4453
#define S_UNLOCK(name)
4454
#endif
4455

    
4456
void set_log_file(const gchar *filename)
4457
{
4458
        S_LOCK(log_fp);
4459
        if (!log_fp) {
4460
                log_fp = g_fopen(filename, "w");
4461
                if (!log_fp)
4462
                        FILE_OP_ERROR(filename, "fopen");
4463
        }
4464
        S_UNLOCK(log_fp);
4465
}
4466

    
4467
void close_log_file(void)
4468
{
4469
        S_LOCK(log_fp);
4470
        if (log_fp) {
4471
                fclose(log_fp);
4472
                log_fp = NULL;
4473
        }
4474
        S_UNLOCK(log_fp);
4475
}
4476

    
4477
static guint log_verbosity_count = 0;
4478

    
4479
void set_log_verbosity(gboolean verbose)
4480
{
4481
        if (verbose)
4482
                log_verbosity_count++;
4483
        else if (log_verbosity_count > 0)
4484
                log_verbosity_count--;
4485
}
4486

    
4487
gboolean get_debug_mode(void)
4488
{
4489
        return debug_mode;
4490
}
4491

    
4492
void set_debug_mode(gboolean enable)
4493
{
4494
        debug_mode = enable;
4495
}
4496

    
4497
static void log_dummy_func(const gchar *str)
4498
{
4499
}
4500

    
4501
static void log_dummy_flush_func(void)
4502
{
4503
}
4504

    
4505
static LogFunc log_print_ui_func = log_dummy_func;
4506
static LogFunc log_message_ui_func = log_dummy_func;
4507
static LogFunc log_warning_ui_func = log_dummy_func;
4508
static LogFunc log_error_ui_func = log_dummy_func;
4509
static LogFlushFunc log_flush_ui_func = log_dummy_flush_func;
4510

    
4511
static LogFunc log_show_status_func = log_dummy_func;
4512

    
4513
void set_log_ui_func(LogFunc print_func, LogFunc message_func,
4514
                     LogFunc warning_func, LogFunc error_func)
4515
{
4516
        log_print_ui_func = print_func;
4517
        log_message_ui_func = message_func;
4518
        log_warning_ui_func = warning_func;
4519
        log_error_ui_func = error_func;
4520
}
4521

    
4522
void set_log_ui_func_full(LogFunc print_func, LogFunc message_func,
4523
                          LogFunc warning_func, LogFunc error_func,
4524
                          LogFlushFunc flush_func)
4525
{
4526
        set_log_ui_func(print_func, message_func, warning_func, error_func);
4527
        log_flush_ui_func = flush_func;
4528
}
4529

    
4530
void set_log_show_status_func(LogFunc status_func)
4531
{
4532
        log_show_status_func = status_func;
4533
}
4534

    
4535
void debug_print(const gchar *format, ...)
4536
{
4537
        va_list args;
4538
        gchar buf[BUFFSIZE];
4539

    
4540
        if (!debug_mode) return;
4541

    
4542
        va_start(args, format);
4543
        g_vsnprintf(buf, sizeof(buf), format, args);
4544
        va_end(args);
4545

    
4546
        g_print("%s", buf);
4547
}
4548

    
4549
void status_print(const gchar *format, ...)
4550
{
4551
        va_list args;
4552
        gchar buf[BUFFSIZE];
4553

    
4554
        va_start(args, format);
4555
        g_vsnprintf(buf, sizeof(buf), format, args);
4556
        va_end(args);
4557

    
4558
        log_show_status_func(buf);
4559
}
4560

    
4561
#define TIME_LEN        11
4562

    
4563
void log_write(const gchar *str, const gchar *prefix)
4564
{
4565
        S_LOCK(log_fp);
4566

    
4567
        if (log_fp) {
4568
                gchar buf[TIME_LEN + 1];
4569
                time_t t;
4570

    
4571
                time(&t);
4572
                strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4573

    
4574
                fputs(buf, log_fp);
4575
                if (prefix)
4576
                        fputs(prefix, log_fp);
4577
                fputs(str, log_fp);
4578
                fflush(log_fp);
4579
        }
4580

    
4581
        S_UNLOCK(log_fp);
4582
}
4583

    
4584
void log_print(const gchar *format, ...)
4585
{
4586
        va_list args;
4587
        gchar buf[BUFFSIZE + TIME_LEN];
4588
        time_t t;
4589

    
4590
        time(&t);
4591
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4592

    
4593
        va_start(args, format);
4594
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4595
        va_end(args);
4596

    
4597
        if (debug_mode) g_print("%s", buf);
4598
        log_print_ui_func(buf);
4599
        S_LOCK(log_fp);
4600
        if (log_fp) {
4601
                fputs(buf, log_fp);
4602
                fflush(log_fp);
4603
        }
4604
        S_UNLOCK(log_fp);
4605
        if (log_verbosity_count)
4606
                log_show_status_func(buf + TIME_LEN);
4607
}
4608

    
4609
void log_message(const gchar *format, ...)
4610
{
4611
        va_list args;
4612
        gchar buf[BUFFSIZE + TIME_LEN];
4613
        time_t t;
4614

    
4615
        time(&t);
4616
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4617

    
4618
        va_start(args, format);
4619
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4620
        va_end(args);
4621

    
4622
        if (debug_mode) g_message("%s", buf + TIME_LEN);
4623
        log_message_ui_func(buf + TIME_LEN);
4624
        S_LOCK(log_fp);
4625
        if (log_fp) {
4626
                fwrite(buf, TIME_LEN, 1, log_fp);
4627
                fputs("* message: ", log_fp);
4628
                fputs(buf + TIME_LEN, log_fp);
4629
                fflush(log_fp);
4630
        }
4631
        S_UNLOCK(log_fp);
4632
        log_show_status_func(buf + TIME_LEN);
4633
}
4634

    
4635
void log_warning(const gchar *format, ...)
4636
{
4637
        va_list args;
4638
        gchar buf[BUFFSIZE + TIME_LEN];
4639
        time_t t;
4640

    
4641
        time(&t);
4642
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4643

    
4644
        va_start(args, format);
4645
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4646
        va_end(args);
4647

    
4648
        g_warning("%s", buf);
4649
        log_warning_ui_func(buf + TIME_LEN);
4650
        S_LOCK(log_fp);
4651
        if (log_fp) {
4652
                fwrite(buf, TIME_LEN, 1, log_fp);
4653
                fputs("** warning: ", log_fp);
4654
                fputs(buf + TIME_LEN, log_fp);
4655
                fflush(log_fp);
4656
        }
4657
        S_UNLOCK(log_fp);
4658
}
4659

    
4660
void log_error(const gchar *format, ...)
4661
{
4662
        va_list args;
4663
        gchar buf[BUFFSIZE + TIME_LEN];
4664
        time_t t;
4665

    
4666
        time(&t);
4667
        strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t));
4668

    
4669
        va_start(args, format);
4670
        g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args);
4671
        va_end(args);
4672

    
4673
        g_warning("%s", buf);
4674
        log_error_ui_func(buf + TIME_LEN);
4675
        S_LOCK(log_fp);
4676
        if (log_fp) {
4677
                fwrite(buf, TIME_LEN, 1, log_fp);
4678
                fputs("*** error: ", log_fp);
4679
                fputs(buf + TIME_LEN, log_fp);
4680
                fflush(log_fp);
4681
        }
4682
        S_UNLOCK(log_fp);
4683
}
4684

    
4685
void log_flush(void)
4686
{
4687
        S_LOCK(log_fp);
4688
        if (log_fp)
4689
                fflush(log_fp);
4690
        S_UNLOCK(log_fp);
4691
        log_flush_ui_func();
4692
}