Skip to content
Permalink
Browse files

Properly convert to/from UTF8 in libzip calls

libzip encodes all the strings it returns in UTF-8 and it expects
them to be passed to it in UTF-8 as well. This works (by chance,
to be honest) on Unix since most Unix hosts use UTF-8 right now.
It, however, fails on Windows which uses either Unicode or ANSI.

This patch, based on one submitted by Jérémie Laval in

https://bugzilla.xamarin.com/show_bug.cgi?id=44038#c1

fixes this issue by properly re-encoding the strings to/from the
current system encoding to/from UTF-8.

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=44268
  • Loading branch information...
grendello committed Sep 16, 2016
1 parent efae5aa commit eeba812d287c74e6d28cf5461f6fd62cff2f6963
Showing with 190 additions and 20 deletions.
  1. +150 −20 Native.cs
  2. +40 −0 Utilities.cs
170 Native.cs
@@ -75,7 +75,17 @@ public static T ZipSourceGetArgs<T> (IntPtr data, UInt64 len)
const string ZIP_LIBNAME = "libzip.dll";

[DllImport (ZIP_LIBNAME, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_open (string path, OpenFlags flags, out ErrorCode errorp);
public static extern IntPtr zip_open (IntPtr path, OpenFlags flags, out ErrorCode errorp);

public static IntPtr zip_open (string path, OpenFlags flags, out ErrorCode errorp)
{
IntPtr utfPath = Utilities.StringToUtf8StringPtr (path);
try {
return zip_open (utfPath, flags, out errorp);
} finally {
Utilities.FreeUtf8StringPtr (utfPath);
}
}

[DllImport (ZIP_LIBNAME, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_open_from_source (IntPtr source, OpenFlags flags, out zip_error_t errorp);
@@ -84,16 +94,46 @@ public static T ZipSourceGetArgs<T> (IntPtr data, UInt64 len)
public static extern void zip_stat_init ([In][Out] zip_stat_t st);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern Int64 zip_name_locate (IntPtr archive, string fname, OperationFlags flags);
public static extern Int64 zip_name_locate (IntPtr archive, IntPtr fname, OperationFlags flags);

public static Int64 zip_name_locate (IntPtr archive, string fname, OperationFlags flags)
{
var utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return zip_name_locate (archive, utfFname, flags);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_fopen (IntPtr archive, string fname, OperationFlags flags);
public static extern IntPtr zip_fopen (IntPtr archive, IntPtr fname, OperationFlags flags);

public static IntPtr zip_fopen (IntPtr archive, string fname, OperationFlags flags)
{
var utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return zip_fopen (archive, utfFname, flags);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_fopen_index (IntPtr archive, UInt64 index, OperationFlags flags);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_fopen_encrypted (IntPtr archive, string fname, OperatingSystem flags, string password);
public static extern IntPtr zip_fopen_encrypted (IntPtr archive, IntPtr fname, OperatingSystem flags, string password);

public static IntPtr zip_fopen_encrypted (IntPtr archive, string fname, OperatingSystem flags, string password)
{
var utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return zip_fopen_encrypted (archive, utfFname, flags, password);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_fopen_index_encrypted (IntPtr archive, UInt64 index, OperationFlags flags, string password);
@@ -111,22 +151,47 @@ public static T ZipSourceGetArgs<T> (IntPtr data, UInt64 len)
public static extern void zip_discard (IntPtr archive);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_stat (IntPtr archive, string fname, OperationFlags flags, out zip_stat_t sb);
public static extern int zip_stat (IntPtr archive, IntPtr fname, OperationFlags flags, out zip_stat_t sb);

public static int zip_stat (IntPtr archive, string fname, OperationFlags flags, out zip_stat_t sb)
{
var utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return zip_stat (archive, utfFname, flags, out sb);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_stat_index (IntPtr archive, UInt64 index, OperationFlags flags, out zip_stat_t sb);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern string zip_file_get_comment (IntPtr archive, UInt64 index, out UInt32 lenp, OperationFlags flags);
[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl, EntryPoint="zip_file_get_comment")]
public static extern IntPtr zip_file_get_comment_ptr (IntPtr archive, UInt64 index, out UInt32 lenp, OperationFlags flags);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern string zip_get_archive_comment (IntPtr archive, out int lenp, OperationFlags flags);
public static string zip_file_get_comment (IntPtr archive, UInt64 index, out UInt32 lenp, OperationFlags flags)
{
return Utilities.Utf8StringPtrToString (zip_file_get_comment_ptr (archive, index, out lenp, flags));
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl, EntryPoint="zip_get_archive_comment")]
public static extern IntPtr zip_get_archive_comment_ptr (IntPtr archive, out int lenp, OperationFlags flags);

public static string zip_get_archive_comment (IntPtr archive, out int lenp, OperationFlags flags)
{
return Utilities.Utf8StringPtrToString (zip_get_archive_comment_ptr (archive, out lenp, flags));
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_get_archive_flag (IntPtr archive, ArchiveGlobalFlags flag, OperationFlags flags);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern string zip_get_name (IntPtr archive, UInt64 index, OperationFlags flags);
[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl, EntryPoint="zip_get_name")]
public static extern IntPtr zip_get_name_ptr (IntPtr archive, UInt64 index, OperationFlags flags);

public static string zip_get_name (IntPtr archive, UInt64 index, OperationFlags flags)
{
return Utilities.Utf8StringPtrToString (zip_get_name_ptr (archive, index, flags));
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern Int64 zip_get_num_entries (IntPtr archive, OperationFlags flags);
@@ -135,7 +200,17 @@ public static T ZipSourceGetArgs<T> (IntPtr data, UInt64 len)
public static extern int zip_set_default_password (IntPtr archive, string password);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_rename (IntPtr archive, UInt64 index, string name);
public static extern int zip_rename (IntPtr archive, UInt64 index, IntPtr name);

public static int zip_rename (IntPtr archive, UInt64 index, string name)
{
var utfName = Utilities.StringToUtf8StringPtr (name);
try {
return zip_rename (archive, index, utfName);
} finally {
Utilities.FreeUtf8StringPtr (utfName);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_delete (IntPtr archive, UInt64 index);
@@ -175,8 +250,13 @@ public static int zip_file_extra_field_set (IntPtr archive, UInt64 index, UInt16
[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern Int16 zip_file_extra_fields_count_by_id (IntPtr archive, UInt64 index, UInt16 extra_field_id, OperationFlags flags);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern string zip_file_strerror (IntPtr file);
[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl, EntryPoint="zip_file_strerror")]
public static extern IntPtr zip_file_strerror_ptr (IntPtr file);

public static string zip_file_strerror (IntPtr file)
{
return Utilities.Utf8StringPtrToString (zip_file_strerror_ptr (file));
}

[DllImport (ZIP_LIBNAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_strerror (IntPtr archive);
@@ -191,7 +271,7 @@ public static int zip_file_extra_field_set (IntPtr archive, UInt64 index, UInt16
public static extern void zip_error_fini (IntPtr ze);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern string zip_error_strerror (IntPtr ze);
public static extern IntPtr zip_error_strerror (IntPtr ze);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void zip_source_free (IntPtr source);
@@ -200,10 +280,30 @@ public static int zip_file_extra_field_set (IntPtr archive, UInt64 index, UInt16
public static extern IntPtr zip_source_zip (IntPtr archive, IntPtr srcarchive, UInt64 srcidx, OpenFlags flags, UInt64 start, UInt64 len);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_source_file (IntPtr archive, string fname, UInt64 start, Int64 len);
public static extern IntPtr zip_source_file (IntPtr archive, IntPtr fname, UInt64 start, Int64 len);

public static IntPtr zip_source_file (IntPtr archive, string fname, UInt64 start, Int64 len)
{
IntPtr utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return Native.zip_source_file (archive, utfFname, start, len);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_source_file_create (string fname, UInt64 start, UInt64 len, out IntPtr error);
public static extern IntPtr zip_source_file_create (IntPtr fname, UInt64 start, UInt64 len, out IntPtr error);

public static IntPtr zip_source_file_create (string fname, UInt64 start, UInt64 len, out IntPtr error)
{
IntPtr utfFname = Utilities.StringToUtf8StringPtr (fname);
try {
return zip_source_file_create (utfFname, start, len, out error);
} finally {
Utilities.FreeUtf8StringPtr (utfFname);
}
}

[DllImport (ZIP_LIBNAME, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zip_source_function (IntPtr source,
@@ -217,16 +317,46 @@ public static int zip_file_extra_field_set (IntPtr archive, UInt64 index, UInt16
public static extern UInt64 zip_source_seek_compute_offset (UInt64 offset, UInt64 length, IntPtr data, UInt64 data_length, out zip_error_t error);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern Int64 zip_dir_add (IntPtr archive, string name, OperationFlags flags);
public static extern Int64 zip_dir_add (IntPtr archive, IntPtr name, OperationFlags flags);

public static Int64 zip_dir_add (IntPtr archive, string name, OperationFlags flags)
{
IntPtr utfName = Utilities.StringToUtf8StringPtr (name);
try {
return zip_dir_add (archive, utfName, flags);
} finally {
Utilities.FreeUtf8StringPtr (utfName);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern Int64 zip_file_add (IntPtr archive, string name, IntPtr source, OperationFlags flags);
public static extern Int64 zip_file_add (IntPtr archive, IntPtr name, IntPtr source, OperationFlags flags);

public static Int64 zip_file_add (IntPtr archive, string name, IntPtr source, OperationFlags flags)
{
IntPtr utfName = Utilities.StringToUtf8StringPtr (name);
try {
return zip_file_add (archive, utfName, source, flags);
} finally {
Utilities.FreeUtf8StringPtr (utfName);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_file_replace (IntPtr archive, UInt64 index, IntPtr source, OperationFlags flags);

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_set_file_comment (IntPtr archive, UInt64 index, string comment, UInt16 len, OperationFlags flags);
public static extern int zip_set_file_comment (IntPtr archive, UInt64 index, IntPtr comment, UInt16 len, OperationFlags flags);

public static int zip_set_file_comment (IntPtr archive, UInt64 index, string comment, UInt16 len, OperationFlags flags)
{
IntPtr utfComment = Utilities.StringToUtf8StringPtr (comment);
try {
return zip_set_file_comment (archive, index, utfComment, len, flags);
} finally {
Utilities.FreeUtf8StringPtr (utfComment);
}
}

[DllImport (ZIP_LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int zip_set_file_compression (IntPtr archive, UInt64 index, CompressionMethod comp, UInt32 comp_flags);
@@ -24,8 +24,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Text;
using System.Runtime.InteropServices;

using Mono.Unix.Native;

namespace Xamarin.Tools.Zip
{
partial class Utilities
@@ -66,6 +69,43 @@ public static ulong UnixTimeFromDateTime (DateTime time)

return (ulong)((time - UnixEpoch).TotalSeconds);
}

public unsafe static IntPtr StringToUtf8StringPtr (string str)
{
if (str == null)
throw new ArgumentNullException (nameof (str));

var encoding = Encoding.UTF8;
int count = encoding.GetByteCount (str);
IntPtr memory = Marshal.AllocHGlobal (count + 1);
fixed (char *pStr = str)
encoding.GetBytes (pStr, str.Length, (byte*)memory, count);
*(((byte*)memory) + count) = 0;
return memory;
}

public unsafe static string Utf8StringPtrToString (IntPtr utf8Str)
{
if (utf8Str == IntPtr.Zero)
return null;

byte* ptr = (byte*)utf8Str.ToPointer ();
byte* p = ptr;
while (*p != 0)
p++;
var len = (int)(p - ptr);
if (len == 0)
return String.Empty;

var bytes = new byte[len];
Marshal.Copy (utf8Str, bytes, 0, len);
return Encoding.UTF8.GetString (bytes);
}

public static void FreeUtf8StringPtr (IntPtr ptr)
{
Marshal.FreeHGlobal (ptr);
}
}
}

0 comments on commit eeba812

Please sign in to comment.
You can’t perform that action at this time.