aboutsummaryrefslogblamecommitdiffstats
path: root/camel/providers/mbox/camel-mbox-utils.c
blob: f341210f71fd8970c62d5d4b384bbeee63d1374d (plain) (tree)

































                                                                           
                                                

















                                                                                        
                   




                              
                               



                                  








































































                                                                        
                                                    






                                                                       
                                                     
















                                                              




                                               


                            
                           
        
                              
                                             

                    
                                                                                
                                                          

 
                                    




                                                                                             










                                                             
                                                  
                                                                                            


                                                                                      









                               

































































































                                                                                                  
                                                                                                                                           














































                                                                                                                  












                                                                                                                              
                                                                           


                                                                             
                                                        




                                                       
 
       

                                              
                                                   

                                         





                                                 

                             
                                      





                                                      

                            

                                                                                      

                                              

                                                  
                          
 









                                                                                              




                                                                                                   
                                                                                          
 
                                             
                        





                                                                       




                                                                                
                                                                                 




                                                          
                        
                                                 
 

                                                                                                
                                                    
                                         



                                                                                               
                                         








                                                                     
         
        
                                                      
                                                                         




















































                                                                                                   





                                                               
                                                  






                                                                                                  
                                                                                             
 
                                                                        
 

                                                        
                                                                                    
 
                                                      

                                                                                
 
                                                            
 
                                                                      
                                             
 
                                                                   

                                          
                                                             
                                                        
 


                                                                                
                                                        




                            
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Various utilities for the mbox provider */

/* 
 * Authors : 
 *   Bertrand Guiheneuf <bertrand@helixcode.com>
 *
 * Copyright (C) 1999 Helix Code.
 *
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */


/* "xev" stands for x-evolution, which is the name of the
 * evolution specific header where are stored informations
 * like : 
 *   - mail status 
 *   - mail uid 
 *  ...
 *
 *
 * The evolution line has the following format :
 *
 *   X-Evolution:XXXXX-X
 *               \___/ \/
 *          UID ---'    `- Status 
 * 
 * the UID is internally used as a 32 bits long integer, but only the first 24 bits are 
 * used. The UID is coded as a string on 4 characters. Each character is a 6 bits 
 * integer coded using the b64 alphabet. 
 *
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>


#include <glib.h>
#include "camel-mbox-utils.h"
#include "camel-mbox-parser.h"
#include "camel-mbox-summary.h"
#include "camel-mime-message.h"
#include "camel/camel-mime-part.h"
#include "camel/camel-multipart.h"
#include "camel/camel-stream-fs.h"

static gchar b64_alphabet[64] = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";



static void 
uid_to_string (guint32 uid, gchar string[4])
{
    
    string [0] = b64_alphabet [(uid >> 18) & 0x3f];
    string [1] = b64_alphabet [(uid >> 12) & 0x3f];
    string [2] = b64_alphabet [(uid >>  6) & 0x3f];
    string [3] = b64_alphabet [(uid      ) & 0x3f];
}


static guint32
string_to_uid (gchar *string)
{
    guint32 i;
    
    i = 
        (((string [0] >= 97) ? ( string [0] - 71 ) :
         ((string [0] >= 65) ? ( string [0] - 65 ) :
          ((string [0] >= 48) ? ( string [0] + 4 ) :
           ((string [0] == 43) ? 62 : 63 )))) << 18)        
        
        + (((string [1] >= 97) ? ( string [1] - 71 ) :
           ((string [1] >= 65) ? ( string [1] - 65 ) :
            ((string [1] >= 48) ? ( string [1] + 4 ) :
             ((string [1] == 43) ? 62 : 63 )))) << 12)
        
        
        + ((((string [2] >= 97) ? ( string [2] - 71 ) :
           ((string [2] >= 65) ? ( string [2] - 65 ) :
            ((string [2] >= 48) ? ( string [2] + 4 ) :
             ((string [2] == 43) ? 62 : 63 ))))) << 6)
        
        
        + (((string [3] >= 97) ? ( string [3] - 71 ) :
           ((string [3] >= 65) ? ( string [3] - 65 ) :
            ((string [3] >= 48) ? ( string [3] + 4 ) :
             ((string [3] == 43) ? 62 : 63 )))));
    
    return i;
    
}


static gchar
flag_to_string (guchar status)
{
    return b64_alphabet [status & 0x3f];
}


static guchar
string_to_flag (gchar string)
{   
    return (string >= 97) ? ( string - 71 ) :
        ((string >= 65) ? ( string - 65 ) :
         ((string >= 48) ? ( string + 4 ) :
          ((string == 43) ? 62 : 63 )));
}





void 
camel_mbox_xev_parse_header_content (gchar header_content[6], 
                     guint32 *uid, 
                     guchar *status)
{
    
    /* we assume that the first 4 characters of the header content 
       are actually the uid stuff. If somebody messed with it ...
       toooo bad. 
    */
    *uid = string_to_uid (header_content);
    *status = string_to_flag (header_content[5]);
}

void 
camel_mbox_xev_write_header_content (gchar header_content[6], 
                     guint32 uid, 
                     guchar status)
{
    uid_to_string (uid, header_content);
    header_content[5] = flag_to_string (status);
    header_content[4] = '-';
}






void 
camel_mbox_copy_file_chunk (gint fd_src,
                gint fd_dest, 
                glong nb_bytes, 
                CamelException *ex)
{
    gchar buffer [1000];
    glong nb_to_read;
    glong nb_read=1, v;
    
    nb_to_read = nb_bytes;
    while (nb_to_read > 0 && nb_read>0) {
        
        do {
            nb_read = read (fd_src, buffer, MIN (1000, nb_to_read));
        } while (nb_read == -1 && errno == EINTR);


        if (nb_read == -1) {
            camel_exception_setv (ex, 
                          CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                          "could not read from the mbox file\n"
                          "Full error is : %s\n",
                          strerror (errno));
            return;
        }


        nb_to_read -= nb_read;

        do {
            v = write (fd_dest, buffer, nb_read);
        } while (v == -1 && errno == EINTR);

        if (v == -1) {
            camel_exception_setv (ex, 
                         CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                         "could not write to the mbox copy file\n"
                          "Full error is : %s\n",
                          strerror (errno));
            return;
        }

        
    }
    
    
    
}

typedef void (*index_data_callback)(ibex *index, char *text, int len, int *left);

/*
  needs to handle encoding?
*/
static void
index_text(ibex *index, char *text, int len, int *left)
{
    /*printf("indexing %.*s\n", len, text);*/

    ibex_index_buffer(index, "message", text, len, left);
/*
    if (left) {
        printf("%d bytes left from indexing\n", *left);
    }
*/
}

/*
  index html data, ignore tags for now.
    could also index attribute values?
    should also handle encoding types ...
    should convert everything to utf8
*/
static void
index_html(ibex *index, char *text, int len, int *left)
{
    static int state = 0;
    char indexbuf[128];
    char *out = indexbuf, *outend = indexbuf+128;
    char *in, *inend;
    int c;

    in = text;
    inend = text+len;

    /*printf("indexing html: %d %d %.*s\n", state, len, len, text);*/

    while (in<inend) {
        c = *in++;
        switch (state) {
        case 0:         /* no tag */
            if (c=='<')
                state = 1;
            else {
                *out++ = c;
                if (out==outend) {
                    index_text(index, indexbuf, out-indexbuf, left);
                    memcpy(indexbuf, indexbuf+(out-indexbuf)-*left, *left);
                    out = indexbuf+*left;
                    printf("** %d bytes left\n", *left);
                }
            }
            break;
        case 1:
            if (c=='>')
                state = 0;
#if 0
            else if (c=='"')
                state = 2;
            break;
        case 2:
            if (c=='"') {
                state = 1;
            }
#endif
            break;
        }
    }
    index_text(index, indexbuf, out-indexbuf, left);
}

static void
index_message_content(ibex *index, CamelDataWrapper *object)
{
    CamelDataWrapper *containee;
    CamelStream *stream;
    int parts, i;
    int len;
    int left;
    char buffer[128];

    containee = camel_medium_get_content_object(CAMEL_MEDIUM(object));

    if (containee) {
        char *type = gmime_content_field_get_mime_type(containee->mime_type);
        index_data_callback callback = NULL;

        /*printf("type = %s\n", type);*/

        if (!strcasecmp(type, "text/plain")) {
            callback = index_text;
        } else if (!strcasecmp(type, "text/html")) {
            callback = index_html;
        } else if (!strncasecmp(type, "multipart/", 10)) {
            parts = camel_multipart_get_number (CAMEL_MULTIPART(containee));
            /*printf("multipart message, scanning contents  %d parts ...\n", parts);*/
            for (i=0;i<parts;i++) {
                index_message_content(index, CAMEL_DATA_WRAPPER (camel_multipart_get_part(CAMEL_MULTIPART(containee), i)));
            }
        } else {
            /*printf("\nunknwon format, ignored\n");*/
        }

        if (callback) {
            int total=0;

            /*printf("reading containee\n");

              printf("containee = %p\n", containee);*/

            stream = camel_data_wrapper_get_output_stream(containee);
            left = 0;

            if (stream) {
                /*printf("stream = %p\n", stream);*/
                while ( (len = camel_stream_read(stream, buffer+left, sizeof(buffer)-left)) > 0) {
                    total = len+left;
                    callback(index, buffer, total, &left);
                    if (left>0) {
                        memcpy(buffer, buffer+total-left, left);
                    }
                }
                callback(index, buffer+total-left, left, NULL);
                
                /*camel_stream_close(stream);*/
                /*printf("\n");*/
            } else {
                g_warning("cannot get stream for message?");
            }
        }

        g_free(type);
    } else {
        printf("no containee?\n");
    }
}


static void
index_message(ibex *index, int fd, CamelMboxParserMessageInfo *mi)
{
    off_t pos;
    CamelStream *stream;
    CamelMimeMessage *message;
    int newfd;

    if (index != NULL) {
        /*printf("indexing message\n %s\n %d for %d bytes\n", mi->from, mi->message_position, mi->size);*/
        pos = lseek(fd, 0, SEEK_CUR);
        
        /* the stream will close the fd we have */
        newfd = dup(fd);
        stream = camel_stream_fs_new_with_fd_and_bounds(newfd, mi->message_position, mi->message_position + mi->size);
        message = camel_mime_message_new_with_session( (CamelSession *)NULL);
        
        camel_data_wrapper_set_input_stream (
            CAMEL_DATA_WRAPPER (message), stream);
        
        index_message_content(index, CAMEL_DATA_WRAPPER (message));
        
        /*  printf("messageid = '%s'\n", message->message_uid);*/
        
        gtk_object_unref (GTK_OBJECT (message));
        gtk_object_unref (GTK_OBJECT (stream));
        
        lseek(fd, pos, SEEK_SET);
    }
}

guint32
camel_mbox_write_xev (CamelMboxFolder *folder,
              gchar *mbox_file_name,
              GArray *summary_information, 
              guint32 *file_size,
              guint32  next_uid, 
              CamelException *ex)
{
    gint cur_msg;
    CamelMboxParserMessageInfo *cur_msg_info;
    gint fd1, fd2;
    guint bytes_to_copy = 0;
    glong cur_pos = 0;
    glong cur_offset = 0;
    glong end_of_last_message = 0;
    glong next_free_uid;
    gchar xev_header[20] = "X-Evolution:XXXX-X\n";
    gchar *tmp_file_name;
    gchar *tmp_file_name_secure;
    gint rename_result;
    gint unlink_result;
    int changed = FALSE;

    tmp_file_name = g_strdup_printf ("%s__.ev_tmp", mbox_file_name);
    tmp_file_name_secure = g_strdup_printf ("%s__.ev_tmp_secure", mbox_file_name);

    fd1 = open (mbox_file_name, O_RDONLY);
    fd2 = open (tmp_file_name, 
            O_WRONLY | O_CREAT | O_TRUNC ,
            0600);

    if (fd2 == -1) {
            camel_exception_setv (ex, 
                         CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                         "could not create the temporary mbox copy file\n"
                          "\t%s\n"
                          "Full error is : %s\n",
                          tmp_file_name,
                          strerror (errno));
            return next_uid;
        }
    
    next_free_uid = next_uid;
    for (cur_msg = 0; cur_msg < summary_information->len; cur_msg++) {
        
        cur_msg_info = (CamelMboxParserMessageInfo *)(summary_information->data) + cur_msg;
        end_of_last_message = cur_msg_info->message_position + cur_msg_info->size;

        if (cur_msg_info->uid == 0) {
            
            bytes_to_copy = cur_msg_info->message_position 
                + cur_msg_info->end_of_headers_offset
                - cur_pos;

            cur_pos = cur_msg_info->message_position 
                + cur_msg_info->end_of_headers_offset;

            cur_msg_info->uid = next_free_uid;          
            index_message(folder->index, fd1, cur_msg_info);
            changed = TRUE;

            camel_mbox_copy_file_chunk (fd1, fd2, bytes_to_copy, ex);
            if (camel_exception_get_id (ex)) {
                close (fd1);
                close (fd2);
                goto end;
            }
            
            cur_msg_info->status = 0;

            camel_mbox_xev_write_header_content (xev_header + 12, next_free_uid, 0);
            next_free_uid++;
            write (fd2, xev_header, 19);
            cur_offset += 19;
            cur_msg_info->size += 19;
            cur_msg_info->x_evolution_offset = cur_msg_info->end_of_headers_offset;
            cur_msg_info->x_evolution = g_strdup_printf ("%.6s", xev_header + 12);
            cur_msg_info->end_of_headers_offset += 19;
            *file_size += 19;
            cur_msg_info->message_position += cur_offset;
        } else {
            cur_msg_info->message_position += cur_offset;
        }
    }

    /* make sure the index is in sync */
    if (changed) {
        ibex_write(folder->index);
    }
    
    bytes_to_copy = end_of_last_message - cur_pos;
        camel_mbox_copy_file_chunk (fd1, fd2, bytes_to_copy, ex);


    /* close the original file as well as the 
       newly created one */
    close (fd1);
    close (fd2);
    


    /* replace the mbox file with the temporary
       file we just created */ 

    /* first rename the old mbox file to a temporary file */
    rename_result = rename (mbox_file_name, tmp_file_name_secure);
    if (rename_result == -1) {
        camel_exception_set (ex, 
                     CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                     "could not rename the mbox file to a temporary file");
        goto end;
    }
    
    /* then rename the newly created mbox file to the name 
       of the original one */
    rename_result = rename (tmp_file_name, mbox_file_name);
    if (rename_result == -1) {
        camel_exception_set (ex, 
                     CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                     "could not rename the X-Evolution fed file to the mbox file");
        goto end;
    }

    /* finally, remove the old renamed mbox file */
    unlink_result = unlink (tmp_file_name_secure);
    if (unlink_result == -1) {
        camel_exception_set (ex, 
                     CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
                     "could not remove the saved original mbox file");
        goto end;
    }


 end: /* free everything and return */
    
    g_free (tmp_file_name);
    g_free (tmp_file_name_secure);
    return next_free_uid;
}






GArray *
parsed_information_to_mbox_summary (GArray *parsed_information)
{
    guint cur_msg;
    CamelMboxParserMessageInfo *cur_msg_info;
    GArray *mbox_summary;
    CamelMboxSummaryInformation *cur_sum_info;

    mbox_summary = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation));
    mbox_summary =  g_array_set_size (mbox_summary, parsed_information->len);

    for (cur_msg = 0; cur_msg < parsed_information->len; cur_msg++) {
        
        cur_msg_info = (CamelMboxParserMessageInfo *)(parsed_information->data) + cur_msg;
        cur_sum_info = (CamelMboxSummaryInformation *)(mbox_summary->data) + cur_msg;

        cur_sum_info->position = cur_msg_info->message_position;

        cur_sum_info->size = cur_msg_info->size;

        cur_sum_info->x_evolution_offset = cur_msg_info->x_evolution_offset;

        cur_sum_info->uid = cur_msg_info->uid;
        cur_sum_info->headers.uid = g_strdup_printf ("%d",
                                 cur_sum_info->uid);

        cur_sum_info->status = cur_msg_info->status;

        cur_sum_info->headers.subject = cur_msg_info->subject;
        cur_msg_info->subject = NULL;

        cur_sum_info->headers.sender =  cur_msg_info->from;
        cur_msg_info->from = NULL;
    
        cur_sum_info->headers.to =  cur_msg_info->to;
        cur_msg_info->to = NULL;        

        /* XXX I'm guessing one of these is wrong. */
        cur_sum_info->headers.received_date =  cur_msg_info->date;
        cur_sum_info->headers.sent_date = g_strdup (cur_msg_info->date);
        cur_msg_info->date = NULL;      
        
    }
    
    return mbox_summary;
}