~ubuntu-branches/ubuntu/quantal/maildir-utils/quantal

« back to all changes in this revision

Viewing changes to src/mu-cmd.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Preining
  • Date: 2010-01-19 20:12:43 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100119201243-d8qmzgxgexhy1bs0
Tags: 0.6~beta1-1
* new upstream release 0.6-beta
  - that merges the several different programs under one binary mu
  - no sqlite storage is used anymore
* debian packaging changes:
  - debian/patches
    . remove all patches
  - remove debian/HOWTO (upstream document) it is completely outdated
  - debian/control:
    . adjust build-dep for gmime-2.4
    . remove build-dep on quilt and sqlite
    . adjust the description to new reality
  - debian/rules:
    . do not try to install doc files that are not present anymore
    . disable quilt adaptions
  - add debian/NEWS that explains that the separate programs are gone

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
** Copyright (C) 2010 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
 
3
**
 
4
** This program is free software; you can redistribute it and/or modify it
 
5
** under the terms of the GNU General Public License as published by the
 
6
** Free Software Foundation; either version 3, or (at your option) any
 
7
** later version.
 
8
**
 
9
** This program is distributed in the hope that it will be useful,
 
10
** but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
** GNU General Public License for more details.
 
13
**
 
14
** You should have received a copy of the GNU General Public License
 
15
** along with this program; if not, write to the Free Software Foundation,
 
16
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
17
**
 
18
*/
 
19
 
 
20
#include <config.h>
 
21
 
 
22
#include <unistd.h>
 
23
#include <stdio.h>
 
24
#include <string.h>
 
25
#include <errno.h>
 
26
 
 
27
#include "mu-msg-gmime.h"
 
28
#include "mu-maildir.h"
 
29
#include "mu-index.h"
 
30
#include "mu-query-xapian.h"
 
31
#include "mu-msg-xapian.h"
 
32
#include "mu-msg-str.h"
 
33
#include "mu-cmd.h"
 
34
#include "mu-util.h"
 
35
 
 
36
static MuCmd 
 
37
cmd_from_string (const char* cmd)
 
38
{
 
39
        if (!cmd)
 
40
                return MU_CMD_UNKNOWN;
 
41
 
 
42
        if (strcmp (cmd, "index") == 0)
 
43
                return MU_CMD_INDEX;
 
44
 
 
45
        /* support some synonyms... */
 
46
        if ((strcmp (cmd, "query") == 0) ||
 
47
            (strcmp (cmd, "find")  == 0) ||
 
48
            (strcmp (cmd, "search") == 0))
 
49
                return MU_CMD_FIND;
 
50
 
 
51
        if (strcmp (cmd, "cleanup") == 0)
 
52
                return MU_CMD_CLEANUP;
 
53
        
 
54
        if ((strcmp (cmd, "mkmdir") == 0) ||
 
55
            (strcmp (cmd, "mkdir") == 0)) 
 
56
                return MU_CMD_MKDIR;
 
57
        
 
58
        /* if (strcmp (cmd, "link") == 0) */
 
59
        /*      return MU_CMD_LINK; */
 
60
        
 
61
        /* if ((strcmp (cmd, "help") == 0) || */
 
62
        /*     (strcmp (cmd, "info") == 0)) */
 
63
        /*      return MU_CMD_HELP; */
 
64
        
 
65
        return MU_CMD_UNKNOWN;
 
66
}
 
67
 
 
68
 
 
69
static gboolean
 
70
print_query (MuQueryXapian *xapian, const gchar *query)
 
71
{
 
72
        char *querystr;
 
73
 
 
74
        MU_WRITE_LOG ("query: '%s' (xquery)", query); 
 
75
        
 
76
        querystr = mu_query_xapian_as_string (xapian, query);
 
77
        g_print ("%s\n", querystr);
 
78
        g_free (querystr);
 
79
 
 
80
        return TRUE;
 
81
}
 
82
 
 
83
 
 
84
static const gchar*
 
85
display_field (MuMsgXapian *row, const MuMsgField* field)
 
86
{
 
87
        gint64 val;
 
88
 
 
89
        switch (mu_msg_field_type(field)) {
 
90
        case MU_MSG_FIELD_TYPE_STRING:
 
91
                return mu_msg_xapian_get_field (row, field);
 
92
 
 
93
        case MU_MSG_FIELD_TYPE_INT:
 
94
        
 
95
                if (mu_msg_field_id(field) == MU_MSG_FIELD_ID_PRIORITY) {
 
96
                        val = mu_msg_xapian_get_field_numeric (row, field);
 
97
                        return mu_msg_str_prio ((MuMsgPriority)val);
 
98
                }
 
99
                
 
100
                if (mu_msg_field_id(field) == MU_MSG_FIELD_ID_FLAGS) {
 
101
                        val = mu_msg_xapian_get_field_numeric (row, field);
 
102
                        return mu_msg_str_flags_s ((MuMsgPriority)val);
 
103
                }
 
104
 
 
105
                return mu_msg_xapian_get_field (row, field); /* as string */
 
106
 
 
107
        case MU_MSG_FIELD_TYPE_TIME_T: 
 
108
                val = mu_msg_xapian_get_field_numeric (row, field);
 
109
                return mu_msg_str_date_s ((time_t)val);
 
110
 
 
111
        case MU_MSG_FIELD_TYPE_BYTESIZE: 
 
112
                val = mu_msg_xapian_get_field_numeric (row, field);
 
113
                return mu_msg_str_size_s ((time_t)val);
 
114
        default:
 
115
                g_return_val_if_reached (NULL);
 
116
        }
 
117
}
 
118
 
 
119
 
 
120
/* returns NULL if there is an error */
 
121
const MuMsgField*
 
122
sort_field_from_string (const char* fieldstr)
 
123
{
 
124
        const MuMsgField *field;
 
125
                
 
126
        field = mu_msg_field_from_name (fieldstr);
 
127
 
 
128
        if (!field && strlen(fieldstr) == 1)
 
129
                field = mu_msg_field_from_shortcut(fieldstr[0]);
 
130
 
 
131
        if (!field)
 
132
                g_printerr ("not a valid sort field: '%s'\n",
 
133
                            fieldstr);
 
134
        return field;
 
135
}
 
136
 
 
137
 
 
138
 
 
139
 
 
140
static gboolean
 
141
print_rows (MuQueryXapian *xapian, const gchar *query, MuConfigOptions *opts)
 
142
{
 
143
        MuMsgXapian *row;
 
144
        const MuMsgField *sortfield;
 
145
 
 
146
        MU_WRITE_LOG ("query: '%s' (rows)", query); 
 
147
        
 
148
        sortfield = NULL;
 
149
        if (opts->sortfield) {
 
150
                sortfield = sort_field_from_string (opts->sortfield);
 
151
                if (!sortfield) /* error occured? */
 
152
                        return FALSE;
 
153
        }
 
154
        
 
155
        row = mu_query_xapian_run (xapian, query, sortfield,
 
156
                                   !opts->descending);
 
157
        if (!row) {
 
158
                g_printerr ("error: running query failed\n");
 
159
                return FALSE;
 
160
        } else if (mu_msg_xapian_is_done (row)) {
 
161
                g_printerr ("No matches found\n");
 
162
                mu_msg_xapian_destroy (row);
 
163
                return FALSE;
 
164
        }
 
165
 
 
166
        /* iterate over the found rows */
 
167
        do  {
 
168
                const char*     fields          = opts->fields;
 
169
                int             printlen        = 0;
 
170
 
 
171
                while (*fields) {
 
172
                        
 
173
                        const MuMsgField* field;
 
174
                        field = mu_msg_field_from_shortcut (*fields);
 
175
                        if (!field || 
 
176
                            !mu_msg_field_is_xapian_enabled (field)) 
 
177
                                printlen += printf ("%c", *fields);
 
178
                        else
 
179
                                printlen += printf ("%s",
 
180
                                                    display_field(row, field));
 
181
                        ++fields;
 
182
                }
 
183
                
 
184
                if (printlen > 0)
 
185
                        printf ("\n");
 
186
                
 
187
                mu_msg_xapian_next (row);
 
188
                
 
189
        } while (!mu_msg_xapian_is_done (row));
 
190
        
 
191
        mu_msg_xapian_destroy (row);
 
192
 
 
193
        return TRUE;
 
194
}
 
195
 
 
196
 
 
197
static gboolean
 
198
do_output_text (MuQueryXapian *xapian, MuConfigOptions* opts,
 
199
                 const gchar **params)
 
200
{
 
201
        gchar *query;
 
202
        gboolean retval = TRUE;
 
203
        
 
204
        query = mu_query_xapian_combine (params, FALSE);
 
205
                
 
206
        /* if xquery is set, we print the xapian query instead of the
 
207
         * output; this is for debugging purposes */
 
208
        if (opts->xquery) 
 
209
                retval = print_query (xapian, query);
 
210
        else
 
211
                retval = print_rows (xapian, query, opts);
 
212
        
 
213
        g_free (query);
 
214
 
 
215
        return retval;
 
216
}
 
217
 
 
218
 
 
219
/* create a linksdir if it not exist yet; if it already existed,
 
220
 * remove old links if opts->clearlinks was specified */
 
221
static gboolean
 
222
create_or_clear_linksdir_maybe (MuConfigOptions* opts)
 
223
{
 
224
        if (access (opts->linksdir, F_OK) != 0) {
 
225
                if (!mu_maildir_mkmdir (opts->linksdir, 0700, TRUE)) 
 
226
                        return FALSE;
 
227
 
 
228
        } else if (opts->clearlinks)
 
229
                mu_maildir_clear_links (opts->linksdir);
 
230
        
 
231
        return TRUE;
 
232
}
 
233
 
 
234
static gboolean
 
235
do_output_links (MuQueryXapian *xapian, MuConfigOptions* opts,
 
236
                  const gchar **params)
 
237
{
 
238
        gchar *query;
 
239
        gboolean retval = TRUE;
 
240
        MuMsgXapian *row;
 
241
        const MuMsgField *pathfield;
 
242
 
 
243
        if (!create_or_clear_linksdir_maybe (opts))
 
244
                return FALSE;
 
245
        
 
246
        query = mu_query_xapian_combine (params, FALSE);
 
247
        
 
248
        MU_WRITE_LOG ("query: '%s' (links)", query); 
 
249
        row = mu_query_xapian_run (xapian, query, NULL, FALSE);
 
250
        if (!row) {
 
251
                g_printerr ("error: running query failed\n");
 
252
                return FALSE;
 
253
        } else if (mu_msg_xapian_is_done (row)) {
 
254
                g_printerr ("No matches found\n");
 
255
                mu_msg_xapian_destroy (row);
 
256
                return FALSE;
 
257
        }
 
258
        
 
259
        pathfield = mu_msg_field_from_id (MU_MSG_FIELD_ID_PATH);
 
260
        
 
261
        /* iterate over the found rows */
 
262
        for (; !mu_msg_xapian_is_done (row); mu_msg_xapian_next (row)) {
 
263
 
 
264
                const char *path;
 
265
                
 
266
                path = mu_msg_xapian_get_field (row, pathfield);
 
267
                if (!path)
 
268
                        continue;
 
269
                        
 
270
                /* this might happen  if the database is not up-to-date */
 
271
                if (access (path, R_OK) != 0) {
 
272
                        g_warning ("Cannot read source message %s: %s",
 
273
                                   path, strerror (errno));
 
274
                        continue;
 
275
                } 
 
276
                
 
277
                if (!mu_maildir_link (path, opts->linksdir))
 
278
                        break;
 
279
        }
 
280
        
 
281
        mu_msg_xapian_destroy (row);
 
282
        g_free (query);
 
283
 
 
284
        return retval;
 
285
}
 
286
 
 
287
 
 
288
static gboolean
 
289
query_params_valid (MuConfigOptions *opts)
 
290
{
 
291
        if (opts->linksdir) 
 
292
                if (opts->xquery) {
 
293
                        g_warning ("Invalid option for '--linksdir'");
 
294
                        return FALSE;
 
295
                }
 
296
                
 
297
        if (!opts->params[0] || !opts->params[1]) {
 
298
                g_warning ("Missing search expression");
 
299
                return FALSE;
 
300
        }
 
301
 
 
302
        if (mu_util_check_dir (opts->xpath, TRUE, FALSE))
 
303
                return TRUE;
 
304
 
 
305
        g_warning ("%s is not a readable Xapian directory", opts->xpath);
 
306
        g_message ("Did you run 'mu index'?");
 
307
        
 
308
        return FALSE;
 
309
}
 
310
 
 
311
 
 
312
gboolean
 
313
cmd_find (MuConfigOptions *opts)
 
314
{
 
315
        MuQueryXapian *xapian;
 
316
        gboolean rv;
 
317
        const gchar **params;
 
318
                
 
319
        if (!query_params_valid (opts))
 
320
                return FALSE;
 
321
        
 
322
        /* first param is 'query', search params are after that */
 
323
        params = (const gchar**)&opts->params[1];
 
324
 
 
325
        mu_msg_gmime_init();
 
326
        
 
327
        xapian = mu_query_xapian_new (opts->xpath);
 
328
        if (!xapian) {
 
329
                g_warning ("Failed to create Xapian query");
 
330
                mu_msg_gmime_uninit ();
 
331
                return FALSE;
 
332
        }
 
333
 
 
334
        if (opts->linksdir)
 
335
                rv = do_output_links (xapian, opts, params);
 
336
        else
 
337
                rv = do_output_text (xapian, opts, params);
 
338
        
 
339
        mu_query_xapian_destroy (xapian);
 
340
        mu_msg_gmime_uninit();
 
341
        
 
342
        return rv;
 
343
}
 
344
 
 
345
 
 
346
static gboolean
 
347
check_index_params (MuConfigOptions *opts)
 
348
{
 
349
        if (opts->linksdir || opts->xquery) {
 
350
                g_warning ("Invalid option(s) for command");
 
351
                return FALSE;
 
352
        }
 
353
        
 
354
        
 
355
        if (!mu_util_check_dir (opts->maildir, TRUE, TRUE)) {
 
356
                g_message ("Please provide a valid Maildir");
 
357
                return FALSE;
 
358
        }
 
359
        
 
360
        return TRUE;
 
361
}
 
362
        
 
363
 
 
364
static MuResult
 
365
index_msg_cb  (MuIndexStats* stats, void *user_data)
 
366
{
 
367
        char *kars="-\\|/";
 
368
        char output[314];
 
369
        
 
370
        static int i = 0;
 
371
        static int len = 0;
 
372
 
 
373
        while (len --> 0) /* note the --> operator :-) */
 
374
                printf ("\b");
 
375
        
 
376
        len = snprintf (output, sizeof(output),
 
377
                        "%c indexing mail; processed: %d; "
 
378
                        "updated/new: %d, cleaned-up: %d",
 
379
                        kars[i % 4], stats->_processed,
 
380
                        stats->_updated, stats->_cleaned_up);
 
381
        g_print ("%s", output);
 
382
        ++i;
 
383
        
 
384
        return MU_OK;
 
385
}
 
386
 
 
387
 
 
388
static gboolean
 
389
cmd_index (MuConfigOptions *opts)
 
390
{
 
391
        int rv;
 
392
 
 
393
        if (!check_index_params (opts))
 
394
                return FALSE;
 
395
 
 
396
        mu_msg_gmime_init ();
 
397
        {
 
398
                MuIndex *midx;
 
399
                MuIndexStats stats;
 
400
                
 
401
                mu_index_stats_clear (&stats);
 
402
                midx = mu_index_new (opts->xpath);
 
403
                
 
404
                if (!midx) {
 
405
                        g_warning ("Indexing failed");
 
406
                        return FALSE;
 
407
                } 
 
408
 
 
409
                g_message ("Indexing messages from %s", opts->maildir);
 
410
                g_message ("Database: %s", opts->xpath);
 
411
                
 
412
                rv = mu_index_run (midx, opts->maildir,
 
413
                                   opts->reindex, &stats,
 
414
                                   opts->quiet ? NULL : index_msg_cb,
 
415
                                   NULL, NULL);
 
416
                if (!opts->nocleanup) {
 
417
                        stats._processed = 0; /* start over */
 
418
                        mu_index_cleanup (midx, &stats,
 
419
                                          opts->quiet ? NULL : index_msg_cb,
 
420
                                          NULL);
 
421
                }
 
422
 
 
423
                if (!opts->quiet) {
 
424
                        index_msg_cb (&stats, NULL);
 
425
                        g_print ("\n");
 
426
                }
 
427
 
 
428
                MU_WRITE_LOG ("processed: %d; updated/new: %d, cleaned-up: %d",
 
429
                              stats._processed, stats._updated,
 
430
                              stats._cleaned_up);
 
431
                
 
432
                mu_index_destroy (midx);
 
433
        }
 
434
        mu_msg_gmime_uninit ();
 
435
        
 
436
        return rv == MU_OK ? TRUE : FALSE;
 
437
}
 
438
 
 
439
 
 
440
MuResult
 
441
cleanup_cb (MuIndexStats *stats, void *user_data)
 
442
{
 
443
        char *kars="-\\|/";
 
444
        char output[100];
 
445
        
 
446
        static int i = 0;
 
447
        static int len = 0;
 
448
 
 
449
        while (len --> 0) 
 
450
                printf ("\b");
 
451
        
 
452
        len = snprintf (output, sizeof(output),
 
453
                        "%c mu is cleaning up the message database; "
 
454
                        "processed: %d; cleaned-up: %d",
 
455
                        kars[i % 4], stats->_processed, stats->_cleaned_up);
 
456
        g_print ("%s", output);
 
457
        ++i;
 
458
 
 
459
        return MU_OK;
 
460
}
 
461
 
 
462
 
 
463
 
 
464
 
 
465
static int
 
466
cmd_mkdir (MuConfigOptions *opts)
 
467
{
 
468
        int i;
 
469
        
 
470
        if (!opts->params[0])
 
471
                return FALSE;  /* shouldn't happen */
 
472
        
 
473
        if (!opts->params[1]) {
 
474
                g_printerr ("usage: mu mkdir <dir> [more dirs]\n");
 
475
                return FALSE;
 
476
        }
 
477
        
 
478
        i = 1;
 
479
        while (opts->params[i]) {
 
480
                MU_WRITE_LOG ("mu_maildir_mkdir (%s, 0755, FALSE)",
 
481
                             opts->params[i]);
 
482
                if (!mu_maildir_mkmdir (opts->params[i], 0755, FALSE))
 
483
                        return FALSE;
 
484
                ++i;
 
485
        }
 
486
 
 
487
        return TRUE;
 
488
}
 
489
 
 
490
 
 
491
 
 
492
#if 0 /* currently, turned off */
 
493
static gboolean
 
494
cmd_link (MuConfigOptions *opts)
 
495
{
 
496
        if (!opts->params[0])
 
497
                return FALSE;  /* shouldn't happen */
 
498
        
 
499
        if (!opts->params[1] || !opts->params[2]) {
 
500
                g_printerr ("usage: mu link <src> <targetdir>\n");
 
501
                return FALSE;
 
502
        }
 
503
 
 
504
        return mu_maildir_link (opts->params[1], opts->params[2]);
 
505
}
 
506
 
 
507
 
 
508
static gboolean
 
509
cmd_help (MuConfigOptions *opts)
 
510
{
 
511
        /* FIXME: get context-sensitive help */
 
512
        _show_version ();
 
513
        return _show_usage (FALSE);
 
514
}
 
515
 
 
516
static gboolean
 
517
cmd_cleanup (MuConfigOptions *opts)
 
518
{
 
519
        int rv;
 
520
        
 
521
        if (!_check_index_params (opts))
 
522
                return FALSE;
 
523
        
 
524
        mu_msg_gmime_init ();
 
525
        {
 
526
                MuIndex *midx;
 
527
                MuIndexStats stats;
 
528
                
 
529
                mu_index_stats_clear (&stats);
 
530
                
 
531
                midx = mu_index_new (opts->xpath);
 
532
                if (!midx) {
 
533
                        g_warning ("Cleanup failed");
 
534
                        return FALSE;
 
535
                }
 
536
                
 
537
                g_message ("Cleaning up removed messages from %s",
 
538
                           opts->xpath);
 
539
                mu_index_cleanup (midx, &stats,
 
540
                                  opts->quiet ? NULL :_cleanup_cb,
 
541
                                  NULL);
 
542
                mu_index_destroy (midx);
 
543
 
 
544
                if (!opts->quiet) {
 
545
                        _cleanup_cb (&stats, NULL);
 
546
                        g_print ("\n");
 
547
                }
 
548
        }
 
549
        
 
550
        mu_msg_gmime_uninit ();
 
551
        
 
552
        return rv == MU_OK ? TRUE : FALSE;
 
553
}
 
554
#endif /* 0 */
 
555
 
 
556
 
 
557
 
 
558
static gboolean
 
559
show_usage (gboolean noerror)
 
560
{
 
561
        const char* usage=
 
562
                "usage: mu [options] command [parameters]\n"
 
563
                "\twhere command is one of index, find, cleanup, mkdir\n"
 
564
                "see mu(1) for more information\n";
 
565
 
 
566
        if (noerror)
 
567
                g_print ("%s", usage);
 
568
        else
 
569
                g_printerr ("%s", usage);
 
570
 
 
571
        return noerror;
 
572
}
 
573
 
 
574
static gboolean
 
575
show_version (void)
 
576
{
 
577
        const char* msg =
 
578
                "mu (mail indexer / searcher version) " VERSION "\n\n"
 
579
                "Copyright (C) 2010 Dirk-Jan C. Binnema\n"
 
580
                "License GPLv3+: GNU GPL version 3 or later "
 
581
                "<http://gnu.org/licenses/gpl.html>.\n\n"
 
582
                "This is free software: you are free to change "
 
583
                "and redistribute it.\n"
 
584
                "There is NO WARRANTY, to the extent permitted by law.";
 
585
 
 
586
        g_print ("%s\n", msg);
 
587
 
 
588
        return TRUE;
 
589
}
 
590
 
 
591
 
 
592
gboolean
 
593
mu_cmd_execute (MuConfigOptions *opts)
 
594
{
 
595
        MuCmd cmd;
 
596
        
 
597
        if (opts->version)
 
598
                return show_version ();
 
599
        
 
600
        if (!opts->params||!opts->params[0]) /* no command? */
 
601
                return show_usage (FALSE);
 
602
        
 
603
        cmd = cmd_from_string (opts->params[0]);
 
604
 
 
605
        switch (cmd) {
 
606
 
 
607
        case MU_CMD_INDEX:   return cmd_index (opts);
 
608
        case MU_CMD_FIND:    return cmd_find (opts);
 
609
 
 
610
        case MU_CMD_MKDIR:   return cmd_mkdir (opts);
 
611
 
 
612
                /* case MU_CMD_CLEANUP: return _cmd_cleanup (opts); */
 
613
                /* case MU_CMD_HELP:    return _cmd_help  (opts); */
 
614
                /* case MU_CMD_LINK:    return _cmd_link  (opts); */
 
615
 
 
616
        case MU_CMD_UNKNOWN: return show_usage (FALSE);
 
617
        default:
 
618
                g_return_val_if_reached (FALSE);
 
619
        }       
 
620
}