doc: Fix documentation of struct data types
[gpgme.git] / src / conversion.c
index f431238..b47d6de 100644 (file)
@@ -1,19 +1,19 @@
 /* conversion.c - String conversion helper functions.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+
    This file is part of GPGME.
 
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
-   
+
    GPGME 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
    Lesser General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 #include <stdlib.h>
 #include <string.h>
-/* Solaris 8 needs sys/types.h before time.h.  */
-#include <sys/types.h>
+#ifdef HAVE_SYS_TYPES_H
+  /* Solaris 8 needs sys/types.h before time.h.  */
+# include <sys/types.h>
+#endif
 #include <time.h>
 #include <errno.h>
 
@@ -93,7 +95,7 @@ _gpgme_decode_c_string (const char *src, char **destp, size_t len)
         string.  */
       dest = malloc (strlen (src) + 1);
       if (!dest)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
 
       *destp = dest;
     }
@@ -148,9 +150,9 @@ _gpgme_decode_c_string (const char *src, char **destp, size_t len)
                    /* A binary zero is not representable in a C
                       string.  */
                    *(dest++) = '\\';
-                   *(dest++) = '0'; 
+                   *(dest++) = '0';
                  }
-               else 
+               else
                  *((unsigned char *) dest++) = val;
                src += 4;
              }
@@ -163,7 +165,7 @@ _gpgme_decode_c_string (const char *src, char **destp, size_t len)
            *(dest++) = *(src++);
            *(dest++) = *(src++);
          }
-        } 
+        }
     }
   *(dest++) = 0;
 
@@ -198,7 +200,7 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
         string.  */
       dest = malloc (strlen (src) + 1);
       if (!dest)
-       return gpg_error_from_errno (errno);
+       return gpg_error_from_syserror ();
 
       *destp = dest;
     }
@@ -214,7 +216,7 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
       else
        {
          int val = _gpgme_hextobyte (&src[1]);
-         
+
          if (val == -1)
            {
              /* Should not happen.  */
@@ -231,9 +233,9 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
                  /* A binary zero is not representable in a C
                     string.  */
                  *(dest++) = '\\';
-                 *(dest++) = '0'; 
+                 *(dest++) = '0';
                }
-             else 
+             else
                *((unsigned char *) dest++) = val;
              src += 3;
            }
@@ -245,6 +247,109 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
 }
 
 
+/* Encode the string SRC with percent escaping and store the result in
+   the buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
+   large enough buffer is allocated with malloc and *DESTP is set to
+   the result.  Currently, LEN is only used to specify if allocation
+   is desired or not, the caller is expected to make sure that *DESTP
+   is large enough if LEN is not zero.  If BINARY is 1, then '\0'
+   characters are allowed in the output.  */
+gpgme_error_t
+_gpgme_encode_percent_string (const char *src, char **destp, size_t len)
+{
+  size_t destlen;
+  char *dest;
+  const char *str;
+
+  destlen = 0;
+  str = src;
+  /* We percent-escape the + character because the user might need a
+     "percent plus" escaped string (special gpg format).  But we
+     percent-escape the space character, which works with and without
+     the special plus format.  */
+  while (*str)
+    {
+      if (*str == '+' || *str == '\"' || *str == '%'
+          || *(const unsigned char *)str <= 0x20)
+        destlen += 3;
+      else
+        destlen++;
+      str++;
+    }
+  /* Terminating nul byte.  */
+  destlen++;
+
+  /* Set up the destination buffer.  */
+  if (len)
+    {
+      if (len < destlen)
+       return gpg_error (GPG_ERR_INTERNAL);
+
+      dest = *destp;
+    }
+  else
+    {
+      /* The converted string will never be larger than the original
+        string.  */
+      dest = malloc (destlen);
+      if (!dest)
+       return gpg_error_from_syserror ();
+
+      *destp = dest;
+    }
+
+  /* Convert the string.  */
+  while (*src)
+    {
+      if (*src == '+' || *src == '\"' || *src == '%'
+          || *(const unsigned char *)src <= 0x20)
+        {
+          snprintf (dest, 4, "%%%02X", *(unsigned char *)src);
+         dest += 3;
+       }
+      else
+       *(dest++) = *src;
+      src++;
+    }
+  *(dest++) = 0;
+
+  return 0;
+}
+
+
+#ifdef HAVE_W32_SYSTEM
+static time_t
+_gpgme_timegm (struct tm *tm)
+{
+  /* This one is thread safe.  */
+  SYSTEMTIME st;
+  FILETIME ft;
+  unsigned long long cnsecs;
+
+  st.wYear   = tm->tm_year + 1900;
+  st.wMonth  = tm->tm_mon  + 1;
+  st.wDay    = tm->tm_mday;
+  st.wHour   = tm->tm_hour;
+  st.wMinute = tm->tm_min;
+  st.wSecond = tm->tm_sec;
+  st.wMilliseconds = 0; /* Not available.  */
+  st.wDayOfWeek = 0;    /* Ignored.  */
+
+  /* System time is UTC thus the conversion is pretty easy.  */
+  if (!SystemTimeToFileTime (&st, &ft))
+    {
+      gpg_err_set_errno (EINVAL);
+      return (time_t)(-1);
+    }
+
+  cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
+           | ft.dwLowDateTime);
+  cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01.  */
+  return (time_t)(cnsecs / 10000000ULL);
+}
+#endif
+
+
 /* Parse the string TIMESTAMP into a time_t.  The string may either be
    seconds since Epoch or in the ISO 8601 format like
    "20390815T143012".  Returns 0 for an empty string or seconds since
@@ -269,6 +374,9 @@ _gpgme_parse_timestamp (const char *timestamp, char **endp)
       if (year < 1900)
         return (time_t)(-1);
 
+      if (endp)
+        *endp = (char*)(timestamp + 15);
+
       /* Fixme: We would better use a configure test to see whether
          mktime can handle dates beyond 2038. */
       if (sizeof (time_t) <= 4 && year >= 2038)
@@ -276,20 +384,21 @@ _gpgme_parse_timestamp (const char *timestamp, char **endp)
 
       memset (&buf, 0, sizeof buf);
       buf.tm_year = year - 1900;
-      buf.tm_mon = atoi_2 (timestamp+4) - 1; 
+      buf.tm_mon = atoi_2 (timestamp+4) - 1;
       buf.tm_mday = atoi_2 (timestamp+6);
       buf.tm_hour = atoi_2 (timestamp+9);
       buf.tm_min = atoi_2 (timestamp+11);
       buf.tm_sec = atoi_2 (timestamp+13);
 
-      if (endp)
-        *endp = (char*)(timestamp + 15);
+#ifdef HAVE_W32_SYSTEM
+      return _gpgme_timegm (&buf);
+#else
 #ifdef HAVE_TIMEGM
       return timegm (&buf);
 #else
       {
         time_t tim;
-        
+
         putenv ("TZ=UTC");
         tim = mktime (&buf);
 #ifdef __GNUC__
@@ -298,123 +407,8 @@ _gpgme_parse_timestamp (const char *timestamp, char **endp)
         return tim;
       }
 #endif /* !HAVE_TIMEGM */
+#endif /* !HAVE_W32_SYSTEM */
     }
   else
     return (time_t)strtoul (timestamp, endp, 10);
 }
-
-
-
-\f
-static struct
-{
-  char *name;
-  gpgme_error_t err;
-} gnupg_errors[] =
-  {
-    { "EOF", GPG_ERR_EOF },
-    { "No_Error", GPG_ERR_NO_ERROR },
-    { "General_Error", GPG_ERR_GENERAL },
-    { "Out_Of_Core", GPG_ERR_ENOMEM },
-    { "Invalid_Value", GPG_ERR_INV_VALUE },
-    { "IO_Error", GPG_ERR_GENERAL },
-    { "Resource_Limit", GPG_ERR_RESOURCE_LIMIT },
-    { "Internal_Error", GPG_ERR_INTERNAL },
-    { "Bad_Certificate", GPG_ERR_BAD_CERT },
-    { "Bad_Certificate_Chain", GPG_ERR_BAD_CERT_CHAIN},
-    { "Missing_Certificate", GPG_ERR_MISSING_CERT },
-    { "No_Data", GPG_ERR_NO_DATA },
-    { "Bad_Signature", GPG_ERR_BAD_SIGNATURE },
-    { "Not_Implemented", GPG_ERR_NOT_IMPLEMENTED },
-    { "Conflict", GPG_ERR_CONFLICT },
-    { "Bug", GPG_ERR_BUG },
-    { "Read_Error", GPG_ERR_GENERAL },
-    { "Write_Error", GPG_ERR_GENERAL },
-    { "Invalid_Line", GPG_ERR_GENERAL },
-    { "Incomplete_Line", GPG_ERR_INCOMPLETE_LINE },
-    { "Invalid_Response", GPG_ERR_INV_RESPONSE },
-    { "Agent_Error", GPG_ERR_AGENT },
-    { "No_Public_Key", GPG_ERR_NO_PUBKEY },
-    { "No_Secret_Key", GPG_ERR_NO_SECKEY },
-    { "File_Open_Error", GPG_ERR_GENERAL },
-    { "File_Create_Error", GPG_ERR_GENERAL },
-    { "File_Error", GPG_ERR_GENERAL },
-    { "Not_Supported", GPG_ERR_NOT_SUPPORTED },
-    { "Invalid_Data", GPG_ERR_INV_DATA },
-    { "Assuan_Server_Fault", GPG_ERR_ASSUAN_SERVER_FAULT },
-    { "Assuan_Error", GPG_ERR_ASSUAN },
-    { "Invalid_Session_Key", GPG_ERR_INV_SESSION_KEY },
-    { "Invalid_Sexp", GPG_ERR_INV_SEXP },
-    { "Unsupported_Algorithm", GPG_ERR_UNSUPPORTED_ALGORITHM },
-    { "No_PIN_Entry", GPG_ERR_NO_PIN_ENTRY },
-    { "PIN_Entry_Error", GPG_ERR_NO_PIN_ENTRY },
-    { "Bad_PIN", GPG_ERR_BAD_PIN },
-    { "Bad_Passphrase", GPG_ERR_BAD_PASSPHRASE },
-    { "Invalid_Name", GPG_ERR_INV_NAME },
-    { "Bad_Public_Key", GPG_ERR_BAD_PUBKEY },
-    { "Bad_Secret_Key", GPG_ERR_BAD_SECKEY },
-    { "Bad_Data", GPG_ERR_BAD_DATA },
-    { "Invalid_Parameter", GPG_ERR_INV_PARAMETER },
-    { "Tribute_to_D_A", GPG_ERR_TRIBUTE_TO_D_A },
-    { "No_Dirmngr", GPG_ERR_NO_DIRMNGR },
-    { "Dirmngr_Error", GPG_ERR_DIRMNGR },
-    { "Certificate_Revoked", GPG_ERR_CERT_REVOKED },
-    { "No_CRL_Known", GPG_ERR_NO_CRL_KNOWN },
-    { "CRL_Too_Old", GPG_ERR_CRL_TOO_OLD },
-    { "Line_Too_Long", GPG_ERR_LINE_TOO_LONG },
-    { "Not_Trusted", GPG_ERR_NOT_TRUSTED },
-    { "Canceled", GPG_ERR_CANCELED },
-    { "Bad_CA_Certificate", GPG_ERR_BAD_CA_CERT },
-    { "Certificate_Expired", GPG_ERR_CERT_EXPIRED },
-    { "Certificate_Too_Young", GPG_ERR_CERT_TOO_YOUNG },
-    { "Unsupported_Certificate", GPG_ERR_UNSUPPORTED_CERT },
-    { "Unknown_Sexp", GPG_ERR_UNKNOWN_SEXP },
-    { "Unsupported_Protection", GPG_ERR_UNSUPPORTED_PROTECTION },
-    { "Corrupted_Protection", GPG_ERR_CORRUPTED_PROTECTION },
-    { "Ambiguous_Name", GPG_ERR_AMBIGUOUS_NAME },
-    { "Card_Error", GPG_ERR_CARD },
-    { "Card_Reset", GPG_ERR_CARD_RESET },
-    { "Card_Removed", GPG_ERR_CARD_REMOVED },
-    { "Invalid_Card", GPG_ERR_INV_CARD },
-    { "Card_Not_Present", GPG_ERR_CARD_NOT_PRESENT },
-    { "No_PKCS15_App", GPG_ERR_NO_PKCS15_APP },
-    { "Not_Confirmed", GPG_ERR_NOT_CONFIRMED },
-    { "Configuration_Error", GPG_ERR_CONFIGURATION },
-    { "No_Policy_Match", GPG_ERR_NO_POLICY_MATCH },
-    { "Invalid_Index", GPG_ERR_INV_INDEX },
-    { "Invalid_Id", GPG_ERR_INV_ID },
-    { "No_Scdaemon", GPG_ERR_NO_SCDAEMON },
-    { "Scdaemon_Error", GPG_ERR_SCDAEMON },
-    { "Unsupported_Protocol", GPG_ERR_UNSUPPORTED_PROTOCOL },
-    { "Bad_PIN_Method", GPG_ERR_BAD_PIN_METHOD },
-    { "Card_Not_Initialized", GPG_ERR_CARD_NOT_INITIALIZED },
-    { "Unsupported_Operation", GPG_ERR_UNSUPPORTED_OPERATION },
-    { "Wrong_Key_Usage", GPG_ERR_WRONG_KEY_USAGE }
-  };
-    
-
-gpgme_error_t
-_gpgme_map_gnupg_error (char *errstr)
-{
-  unsigned int i;
-  gpgme_error_t err = gpg_err_make (GPG_ERR_SOURCE_GPG, GPG_ERR_GENERAL);
-
-  /* Future version of GnuPG might return the error code directly, so
-     we first test for a a numerical value and use that verbatim.
-     Note that this numerical value might be followed by an
-     underschore and the textual representation of the error code. */
-  if (*errstr >= '0' && *errstr <= '9')
-    return strtoul (errstr, NULL, 10);
-
-  /* Well, this is a token, use the mapping table to get the error.
-     The drawback is that we won't receive an error source and have to
-     use GPG as source. */
-  for (i = 0; i < DIM (gnupg_errors); i++)
-    if (!strcmp (gnupg_errors[i].name, errstr))
-      err = gpg_err_make (GPG_ERR_SOURCE_GPG, gnupg_errors[i].err);
-
-  TRACE3 (DEBUG_CTX, "_gpgme_map_gnupg_error", 0,
-         "mapped %s to %s <%s>", errstr, gpgme_strerror (err),
-         gpgme_strsource (err));
-  return err;
-}