Don't call strncpy with mismatching length
[claws.git] / src / vcard.c
index 04a5eefdf7eeccc0e56fa07f9d3404909089c0e9..2e8fce511c1845509628b5159e570912196cdd8f 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 2001-2007 Match Grun and the Claws Mail team
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001-2015 Match Grun and the Claws Mail team
  *
  * 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
  *
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
- * 
  */
 
 /*
  * Functions necessary to access vCard files. vCard files are used
  * by GnomeCard for addressbook, and Netscape for sending business
- * card information. Refer to RFC2426 for more information.
+ * card information. Refer to http://www.imc.org/pdi/vcard-21.txt and
+ * RFC2426 for more information.
  */
 
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
 #include <glib.h>
 #include <sys/stat.h>
 #include <string.h>
@@ -33,6 +38,9 @@
 #include "addrcache.h"
 #include "adbookbase.h"
 #include "utils.h"
+#include "codeconv.h"
+#include "quoted-printable.h"
+#include "file-utils.h"
 
 #define GNOMECARD_DIR     ".gnome"
 #define GNOMECARD_FILE    "GnomeCard"
@@ -61,17 +69,17 @@ VCardFile *vcard_create() {
 * Properties...
 */
 void vcard_set_name( VCardFile* cardFile, const gchar *value ) {
-       g_return_if_fail( cardFile != NULL );
+       cm_return_if_fail( cardFile != NULL );
        addrcache_set_name( cardFile->addressCache, value );
 }
 void vcard_set_file( VCardFile* cardFile, const gchar *value ) {
-       g_return_if_fail( cardFile != NULL );
+       cm_return_if_fail( cardFile != NULL );
        addrcache_refresh( cardFile->addressCache );
        cardFile->path = mgu_replace_string( cardFile->path, value );
        g_strstrip( cardFile->path );
 }
 void vcard_set_accessed( VCardFile *cardFile, const gboolean value ) {
-       g_return_if_fail( cardFile != NULL );
+       cm_return_if_fail( cardFile != NULL );
        cardFile->addressCache->accessFlag = value;
 }
 
@@ -80,13 +88,13 @@ void vcard_set_accessed( VCardFile *cardFile, const gboolean value ) {
 * Return: TRUE if file was modified.
 */
 gboolean vcard_get_modified( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, FALSE );
+       cm_return_val_if_fail( cardFile != NULL, FALSE );
        cardFile->addressCache->modified =
                addrcache_check_file( cardFile->addressCache, cardFile->path );
        return cardFile->addressCache->modified;
 }
 gboolean vcard_get_accessed( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, FALSE );
+       cm_return_val_if_fail( cardFile != NULL, FALSE );
        return cardFile->addressCache->accessFlag;
 }
 
@@ -95,43 +103,32 @@ gboolean vcard_get_accessed( VCardFile *cardFile ) {
 * Return: TRUE if file was read.
 */
 gboolean vcard_get_read_flag( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, FALSE );
+       cm_return_val_if_fail( cardFile != NULL, FALSE );
        return cardFile->addressCache->dataRead;
 }
-void vcard_set_read_flag( VCardFile *cardFile, const gboolean value ) {
-       g_return_if_fail( cardFile != NULL );
-       cardFile->addressCache->dataRead = value;
-}
 
 /*
 * Return status code from last file operation.
 * Return: Status code.
 */
 gint vcard_get_status( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, -1 );
+       cm_return_val_if_fail( cardFile != NULL, -1 );
        return cardFile->retVal;
 }
 
 ItemFolder *vcard_get_root_folder( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, NULL );
+       cm_return_val_if_fail( cardFile != NULL, NULL );
        return addrcache_get_root_folder( cardFile->addressCache );
 }
 gchar *vcard_get_name( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, NULL );
+       cm_return_val_if_fail( cardFile != NULL, NULL );
        return addrcache_get_name( cardFile->addressCache );
 }
 
-/*
-* Refresh internal variables to force a file read.
-*/
-void vcard_force_refresh( VCardFile *cardFile ) {
-       addrcache_refresh( cardFile->addressCache );
-}
-
 /*
 * Create new cardfile object for specified file.
 */
-VCardFile *vcard_create_path( const gchar *path ) {
+static VCardFile *vcard_create_path( const gchar *path ) {
        VCardFile *cardFile;
        cardFile = vcard_create();
        vcard_set_file(cardFile, path);
@@ -142,10 +139,10 @@ VCardFile *vcard_create_path( const gchar *path ) {
 * Free up cardfile object by releasing internal memory.
 */
 void vcard_free( VCardFile *cardFile ) {
-       g_return_if_fail( cardFile != NULL );
+       cm_return_if_fail( cardFile != NULL );
 
        /* Close file */
-       if( cardFile->file ) fclose( cardFile->file );
+       if( cardFile->file ) claws_fclose( cardFile->file );
 
        /* Clear cache */
        addrcache_clear( cardFile->addressCache );
@@ -172,12 +169,12 @@ void vcard_free( VCardFile *cardFile ) {
 * return: TRUE if file opened successfully.
 */
 static gint vcard_open_file( VCardFile* cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, -1 );
+       cm_return_val_if_fail( cardFile != NULL, -1 );
 
        /* g_print( "Opening file\n" ); */
        cardFile->addressCache->dataRead = FALSE;
        if( cardFile->path ) {
-               cardFile->file = g_fopen( cardFile->path, "rb" );
+               cardFile->file = claws_fopen( cardFile->path, "rb" );
                if( ! cardFile->file ) {
                        /* g_printerr( "can't open %s\n", cardFile->path ); */
                        cardFile->retVal = MGU_OPEN_FILE;
@@ -201,8 +198,8 @@ static gint vcard_open_file( VCardFile* cardFile ) {
 * Close file.
 */
 static void vcard_close_file( VCardFile *cardFile ) {
-       g_return_if_fail( cardFile != NULL );
-       if( cardFile->file ) fclose( cardFile->file );
+       cm_return_if_fail( cardFile != NULL );
+       if( cardFile->file ) claws_fclose( cardFile->file );
        cardFile->file = NULL;
 }
 
@@ -212,7 +209,7 @@ static void vcard_close_file( VCardFile *cardFile ) {
 */
 static gchar *vcard_read_line( VCardFile *cardFile ) {
        while( *cardFile->bufptr == '\n' || *cardFile->bufptr == '\0' ) {
-               if( fgets( cardFile->buffer, VCARDBUFSIZE, cardFile->file ) == NULL )
+               if( claws_fgets( cardFile->buffer, VCARDBUFSIZE, cardFile->file ) == NULL )
                        return NULL;
                g_strstrip( cardFile->buffer );
                cardFile->bufptr = cardFile->buffer;
@@ -238,7 +235,7 @@ static gchar *vcard_get_line( VCardFile *cardFile ) {
        start = cardFile->bufptr;
        len = strlen( start );
        end = start + len;
-       strncpy( buf, start, len );
+       memcpy( buf, start, len );
        buf[ len ] = '\0';
        g_strstrip(buf);
        cardFile->bufptr = end + 1;
@@ -251,10 +248,10 @@ static gchar *vcard_get_line( VCardFile *cardFile ) {
 * Free linked lists of character strings.
 */
 static void vcard_free_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList* listID ) {
-       mgu_free_list( listName );
-       mgu_free_list( listAddr );
-       mgu_free_list( listRem );
-       mgu_free_list( listID );
+       g_slist_free_full( listName, g_free );
+       g_slist_free_full( listAddr, g_free );
+       g_slist_free_full( listRem, g_free );
+       g_slist_free_full( listID, g_free );
 }
 
 /*
@@ -278,7 +275,7 @@ static gchar *vcard_read_qp( VCardFile *cardFile, char *tagvalue ) {
        line = mgu_list_coalesce( listQP );
 
        /* Clean up */
-       mgu_free_list( listQP );
+       g_slist_free_full( listQP, g_free );
        listQP = NULL;
        return line;
 }
@@ -291,14 +288,15 @@ static gchar *vcard_get_tagname( char* line, gchar dlm ) {
        gint len = 0;
        gchar *tag = NULL;
        gchar *lptr = line;
-
+       gchar *down;
        while( *lptr++ ) {
                if( *lptr == dlm ) {
                        len = lptr - line;
                        tag = g_strndup( line, len+1 );
                        tag[ len ] = '\0';
-                       g_strdown( tag );
-                       return tag;
+                       down = g_utf8_strdown( tag, -1 );
+                       g_free(tag);
+                       return down;
                }
        }
        return tag;
@@ -353,8 +351,8 @@ static void vcard_build_items(
                        if( *str != '\0' ) {
                                ItemEMail *email = addritem_create_item_email();
                                addritem_email_set_address( email, str );
-                               str = nodeRemarks->data;
                                if( nodeRemarks ) {
+                                       str = nodeRemarks->data;
                                        if( str ) {
                                                if( g_utf8_collate( str, "internet" ) != 0 ) {
                                                        if( *str != '\0' )
@@ -385,48 +383,25 @@ static void vcard_build_items(
 }
 
 /* Unescape characters in quoted-printable string. */
-static void vcard_unescape_qp( gchar *value ) {
-       gchar *ptr, *src, *dest;
-       gint d, v = 0;
-       gchar ch;
-       gboolean gotch;
-       ptr = value;
-       while( *ptr ) {
-               gotch = FALSE;
-               if( *ptr == '=' ) {
-                       v = 0;
-                       ch = *(ptr + 1);
-                       if( ch ) {
-                               if( ch > '0' && ch < '8' ) v = ch - '0';
-                       }
-                       d = -1;
-                       ch = *(ptr + 2);
-                       if( ch ) {
-                               if( ch > '\x60' ) ch -= '\x20';
-                               if( ch > '0' && ch < ' ' ) d = ch - '0';
-                               d = ch - '0';
-                               if( d > 9 ) d -= 7;
-                               if( d > -1 && d < 16 ) {
-                                       v = ( 16 * v ) + d;
-                                       gotch = TRUE;
-                               }
-                       }
-               }
-               if( gotch ) {
-                       /* Replace = with char and move down in buffer */
-                       *ptr = v;
-                       src = ptr + 3;
-                       dest = ptr + 1;
-                       while( *src ) {
-                               *dest++ = *src++;
-                       }
-                       *dest = '\0';
-               }
-               ptr++;
+static gchar *vcard_unescape_qp( gchar *value ) {
+       gchar *res = NULL;
+       gint len;
+       if (value == NULL)
+               return NULL;
+               
+       len = strlen(value);
+       res = g_malloc(len);
+       qp_decode_const(res, len-1, value);
+       if (!g_utf8_validate(res, -1, NULL)) {
+               gchar *mybuf = g_malloc(strlen(res)*2 +1);
+               conv_localetodisp(mybuf, strlen(res)*2 +1, res);
+               g_free(res);
+               res = mybuf;
        }
+       return res;
 }
 
-/*
+ /*
 * Read file data into root folder.
 * Note that one vCard can have multiple E-Mail addresses (MAIL tags);
 * these are broken out into separate address items. An address item
@@ -473,10 +448,15 @@ static void vcard_read_file( VCardFile *cardFile ) {
                /* g_print( "\ttype:  %s\n", tagtype ); */
                /* g_print( "\tvalue: %s\n", tagvalue ); */
 
-               if( g_utf8_collate( tagtype, VCARD_TYPE_QP ) == 0 ) {
+               if( g_utf8_collate( tagtype, VCARD_TYPE_QP ) == 0
+                   || g_utf8_collate( tagtype, VCARD_TYPE_E_QP ) == 0
+                   || g_utf8_collate( tagtype, VCARD_TYPE_CS_UTF8_E_QP ) == 0) {
+                       gchar *tmp;
                        /* Quoted-Printable: could span multiple lines */
                        tagvalue = vcard_read_qp( cardFile, tagvalue );
-                       vcard_unescape_qp( tagvalue );
+                       tmp = vcard_unescape_qp( tagvalue );
+                       g_free(tagvalue);
+                       tagvalue=tmp;
                        /* g_print( "QUOTED-PRINTABLE !!! final\n>%s<\n", tagvalue ); */
                }
 
@@ -529,7 +509,7 @@ static void vcard_read_file( VCardFile *cardFile ) {
 */
 /* ============================================================================================ */
 gint vcard_read_data( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, -1 );
+       cm_return_val_if_fail( cardFile != NULL, -1 );
 
        cardFile->retVal = MGU_SUCCESS;
        cardFile->addressCache->accessFlag = FALSE;
@@ -554,7 +534,7 @@ gint vcard_read_data( VCardFile *cardFile ) {
 * Return link list of persons.
 */
 GList *vcard_get_list_person( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, NULL );
+       cm_return_val_if_fail( cardFile != NULL, NULL );
        return addrcache_get_list_person( cardFile->addressCache );
 }
 
@@ -564,7 +544,7 @@ GList *vcard_get_list_person( VCardFile *cardFile ) {
 * Return: NULL.
 */
 GList *vcard_get_list_folder( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, NULL );
+       cm_return_val_if_fail( cardFile != NULL, NULL );
        return NULL;
 }
 
@@ -575,37 +555,10 @@ GList *vcard_get_list_folder( VCardFile *cardFile ) {
 * Return: List of items, or NULL if none.
 */
 GList *vcard_get_all_persons( VCardFile *cardFile ) {
-       g_return_val_if_fail( cardFile != NULL, NULL );
+       cm_return_val_if_fail( cardFile != NULL, NULL );
        return addrcache_get_all_persons( cardFile->addressCache );
 }
 
-/*
-* Validate that all parameters specified.
-* Return: TRUE if data is good.
-*/
-gboolean vcard_validate( const VCardFile *cardFile ) {
-       gboolean retVal;
-       gchar *name;
-
-       g_return_val_if_fail( cardFile != NULL, FALSE );
-
-       retVal = TRUE;
-       if( cardFile->path ) {
-               if( strlen( cardFile->path ) < 1 ) retVal = FALSE;
-       }
-       else {
-               retVal = FALSE;
-       }
-       name = addrcache_get_name( cardFile->addressCache );
-       if( name ) {
-               if( strlen( name ) < 1 ) retVal = FALSE;
-       }
-       else {
-               retVal = FALSE;
-       }
-       return retVal;
-}
-
 #define WORK_BUFLEN 1024
 
 /*
@@ -616,7 +569,7 @@ gboolean vcard_validate( const VCardFile *cardFile ) {
 gchar *vcard_find_gnomecard( void ) {
        const gchar *homedir;
        gchar buf[ WORK_BUFLEN ];
-       gchar str[ WORK_BUFLEN ];
+       gchar str[ WORK_BUFLEN + 1 ];
        gchar *fileSpec;
        gint len, lenlbl, i;
        FILE *fp;
@@ -624,7 +577,7 @@ gchar *vcard_find_gnomecard( void ) {
        homedir = get_home_dir();
        if( ! homedir ) return NULL;
 
-       strcpy( str, homedir );
+       strncpy( str, homedir, WORK_BUFLEN );
        len = strlen( str );
        if( len > 0 ) {
                if( str[ len-1 ] != G_DIR_SEPARATOR ) {
@@ -632,21 +585,21 @@ gchar *vcard_find_gnomecard( void ) {
                        str[ ++len ] = '\0';
                }
        }
-       strcat( str, GNOMECARD_DIR );
-       strcat( str, G_DIR_SEPARATOR_S );
-       strcat( str, GNOMECARD_FILE );
+       strncat( str, GNOMECARD_DIR, WORK_BUFLEN - strlen(str) );
+       strncat( str, G_DIR_SEPARATOR_S, WORK_BUFLEN - strlen(str) );
+       strncat( str, GNOMECARD_FILE, WORK_BUFLEN - strlen(str) );
 
        fileSpec = NULL;
-       if( ( fp = g_fopen( str, "rb" ) ) != NULL ) {
+       if( ( fp = claws_fopen( str, "rb" ) ) != NULL ) {
                /* Read configuration file */
                lenlbl = strlen( GNOMECARD_SECTION );
-               while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
+               while( claws_fgets( buf, sizeof( buf ), fp ) != NULL ) {
                        if( 0 == g_ascii_strncasecmp( buf, GNOMECARD_SECTION, lenlbl ) ) {
                                break;
                        }
                }
 
-               while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
+               while( claws_fgets( buf, sizeof( buf ), fp ) != NULL ) {
                        g_strchomp( buf );
                        if( buf[0] == '[' ) break;
                        for( i = 0; i < lenlbl; i++ ) {
@@ -658,7 +611,7 @@ gchar *vcard_find_gnomecard( void ) {
                                }
                        }
                }
-               fclose( fp );
+               claws_fclose( fp );
        }
 
        if( fileSpec == NULL ) {
@@ -715,9 +668,12 @@ gint vcard_test_read_file( const gchar *fileSpec ) {
                        }
 
                        if( g_utf8_collate( tagtype, VCARD_TYPE_QP ) == 0 ) {
+                               gchar *tmp;
                                /* Quoted-Printable: could span multiple lines */
                                tagvalue = vcard_read_qp( cardFile, tagvalue );
-                               vcard_unescape_qp( tagvalue );
+                               tmp = vcard_unescape_qp( tagvalue );
+                               g_free(tagvalue);
+                               tagvalue=tmp;
                        }
                        if( g_utf8_collate( tagname, VCARD_TAG_START ) == 0 &&
                                g_ascii_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {