usingnamespace @import("../global.zig"); const std = @import("std"); const assert = std.debug.assert; const mem = std.mem; // https://github.com/Vexu/zuri/blob/master/src/zuri.zig#L61-L127 pub const PercentEncoding = struct { /// possible errors for decode and encode pub const EncodeError = error{ InvalidCharacter, OutOfMemory, }; /// returns true if c is a hexadecimal digit pub fn isHex(c: u8) bool { return switch (c) { '0'...'9', 'a'...'f', 'A'...'F' => true, else => false, }; } /// returns true if str starts with a valid path character or a percent encoded octet pub fn isPchar(str: []const u8) bool { assert(str.len > 0); return switch (str[0]) { 'a'...'z', 'A'...'Z', '0'...'9', '-', '.', '_', '~', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@' => true, '%' => str.len > 3 and isHex(str[1]) and isHex(str[2]), else => false, }; } /// decode path if it is percent encoded pub fn decode(allocator: *Allocator, path: []const u8) EncodeError!?[]u8 { var ret: ?[]u8 = null; errdefer if (ret) |some| allocator.free(some); var ret_index: usize = 0; var i: usize = 0; while (i < path.len) : (i += 1) { if (path[i] == '%') { if (!isPchar(path[i..])) { return error.InvalidCharacter; } if (ret == null) { ret = try allocator.alloc(u8, path.len); mem.copy(u8, ret.?, path[0..i]); ret_index = i; } // charToDigit can't fail because the chars are validated earlier var new = (std.fmt.charToDigit(path[i + 1], 16) catch unreachable) << 4; new |= std.fmt.charToDigit(path[i + 2], 16) catch unreachable; ret.?[ret_index] = new; ret_index += 1; i += 2; } else if (path[i] != '/' and !isPchar(path[i..])) { return error.InvalidCharacter; } else if (ret != null) { ret.?[ret_index] = path[i]; ret_index += 1; } } if (ret) |some| return allocator.shrink(some, ret_index); return null; } /// percent encode if path contains characters not allowed in paths pub fn encode(allocator: *Allocator, path: []const u8) EncodeError!?[]u8 { var ret: ?[]u8 = null; var ret_index: usize = 0; for (path) |c, i| { if (c != '/' and !isPchar(path[i..])) { if (ret == null) { ret = try allocator.alloc(u8, path.len * 3); mem.copy(u8, ret.?, path[0..i]); ret_index = i; } const hex_digits = "0123456789ABCDEF"; ret.?[ret_index] = '%'; ret.?[ret_index + 1] = hex_digits[(c & 0xF0) >> 4]; ret.?[ret_index + 2] = hex_digits[c & 0x0F]; ret_index += 3; } else if (ret != null) { ret.?[ret_index] = c; ret_index += 1; } } if (ret) |some| return allocator.shrink(some, ret_index); return null; } }; pub const MimeType = enum { Unsupported, TextCSS, TextJavaScript, ApplicationJSON, pub const Map = std.ComptimeStringMap(MimeType, .{ .{ "text/css", MimeType.TextCSS }, .{ "text/javascript", MimeType.TextJavaScript }, .{ "application/json", MimeType.ApplicationJSON }, }); pub fn decode(str: string) MimeType { // Remove things like ";charset=utf-8" var mime_type = str; if (strings.indexOfChar(mime_type, ';')) |semicolon| { mime_type = mime_type[0..semicolon]; } return Map.get(mime_type) orelse MimeType.Unsupported; } }; pub const DataURL = struct { mime_type: string, data: string, is_base64: bool = false, pub fn parse(url: string) ?DataURL { if (!strings.startsWith(url, "data:")) { return null; } const comma = strings.indexOfChar(url, ',') orelse return null; var parsed = DataURL{ .mime_type = url["data:".len..comma], .data = url[comma + 1 .. url.len], }; if (strings.endsWith(parsed.mime_type, ";base64")) { parsed.mime_type = parsed.mime_type[0..(parsed.mime_type.len - ";base64".len)]; parsed.is_base64 = true; } return parsed; } pub fn decode_mime_type(d: DataURL) MimeType { return MimeType.decode(d.mime_type); } pub fn decode_data(d: *DataURL, allocator: *std.mem.Allocator, url: string) !string { // Try to read base64 data if (d.is_base64) { const size = try std.base64.standard.Decoder.calcSizeForSlice(d.data); var buf = try allocator.alloc(u8, size); try std.base64.standard.Decoder.decode(buf, d.data); return buf; } // Try to read percent-escaped data return try PercentEncoding.decode(allocator, url); } }; /+2 2018-03-01Entries date should contains user timezone (API)Gravatar Frédéric Guillot 5-17/+146 2018-02-27Improve parser error messagesGravatar Frédéric Guillot 11-61/+61 2018-02-27Support localized feed errors generated by background workersGravatar Frédéric Guillot 18-65/+59 2018-02-27Add Simplified Chinese LocalizationGravatar ReVanTis, Zhao 3-1/+446 2018-02-25Add Nunux Keeper integrationGravatar Nicolas Carlier 20-16/+170 2018-02-23Remove parentheses around feed error messagesGravatar Frédéric Guillot 2-4/+4 2018-02-23Print info message if DATABASE_URL is not setGravatar Frédéric Guillot 2-5/+17 2018-02-19Update ChangeLog2.0.3Gravatar Frédéric Guillot 1-0/+10 2018-02-19Update Polish translationGravatar Sebastian Korotkiewicz 2-96/+96 2018-02-17Handle Atom feeds with HTML titleGravatar Frédéric Guillot 2-2/+92 2018-02-17There are more than 2 plurals in PolishGravatar Frédéric Guillot 2-8/+8 2018-02-17Add Polish translationGravatar Sebastian Korotkiewicz 3-1/+444 2018-02-10Change color of <q> tags for black themeGravatar Frédéric Guillot 2-3/+7 2018-02-10Add indexesGravatar Frédéric Guillot 3-2/+8 2018-02-09Update German translationGravatar stratmaster 2-10/+18 2018-02-08Improve error handling for HTTP clientGravatar Frédéric Guillot 5-7/+62 2018-02-08Add missing German stringGravatar stratmaster 2-4/+6 2018-02-07Strip invalid XML characters to avoid parsing errorsGravatar Frédéric Guillot 1-1/+26 2018-02-07Remove period for feed errorsGravatar Frédéric Guillot 4-23/+23 2018-02-07Improve error handling when the response is emptyGravatar Frédéric Guillot 5-6/+28 2018-02-07Add missing French translationGravatar stratmaster 2-2/+4 2018-02-05Update ChangeLog2.0.2Gravatar Frédéric Guillot 1-0/+14 2018-02-04Add support for Let's Encrypt http-01 challengeGravatar Frédéric Guillot 32-286/+2042 2018-02-04Move template functions outside engine (refactoring)Gravatar Frédéric Guillot 9-214/+254 2018-02-04Take timezone into consideration when calculating relative timeGravatar Frédéric Guillot 16-58/+91 2018-02-03Add support for HTTP Strict Transport Security headerGravatar Frédéric Guillot 4-3/+33 2018-02-03Add support for base URLs with subfoldersGravatar Frédéric Guillot 10-23/+105 2018-02-01Update German translationGravatar stratmaster 2-4/+12 2018-01-31Add missing about menu in settingsGravatar Frédéric Guillot 5-5/+29 2018-01-31Show API URL endpoints in user interfaceGravatar Frédéric Guillot 11-14/+99 2018-01-29Do not update entry date while refreshing a feedGravatar Frédéric Guillot 1-4/+5 2018-01-29Enable debug mode for integration testsGravatar Frédéric Guillot 1-1/+1 2018-01-29Add the possiblity to enable debug mode with an environment variableGravatar Frédéric Guillot 2-1/+6 2018-01-29Keep code base tidyGravatar Frédéric Guillot 5-18/+16 2018-01-29Add flag to enable debug loggingGravatar Rogier Lommers 2-7/+64 2018-01-25Improve unread counter updatesGravatar Mahendra Kalkura 1-6/+35