2
0

Marshal nulls as empty text & document data structures.

Documented the data fields in the marshal structure to avoid value
ambiguity.

NULL values for eg. identicon shouldn't serialize to flat (mpsites) file
as "(null)" but rather as empty "".

Parsing header values from the flat file that are empty yielded NULL
instead of the empty string since mpw_strtok was needlessly aggressive
on that front.
This commit is contained in:
Maarten Billemont 2020-02-28 17:25:20 -05:00
parent f659c0936e
commit b15417aa31
4 changed files with 69 additions and 17 deletions

View File

@ -31,7 +31,7 @@ char *mpw_get_token(const char **in, const char *eol, const char *delim) {
// Find characters up to the first delim.
size_t len = strcspn( *in, delim );
char *token = len && len <= (size_t)(eol - *in)? mpw_strndup( *in, len ): NULL;
char *token = len <= (size_t)(eol - *in)? mpw_strndup( *in, len ): NULL;
// Advance past the delimitor.
*in = min( eol, *in + len + 1 );

View File

@ -552,12 +552,12 @@ static const char *mpw_marshal_write_flat(
mpw_string_pushf( &out, "##\n" );
mpw_string_pushf( &out, "# Format: %d\n", 1 );
mpw_string_pushf( &out, "# Date: %s\n", mpw_marshal_data_get_str( data, "export", "date", NULL ) );
mpw_string_pushf( &out, "# User Name: %s\n", mpw_marshal_data_get_str( data, "user", "full_name", NULL ) );
mpw_string_pushf( &out, "# Full Name: %s\n", mpw_marshal_data_get_str( data, "user", "full_name", NULL ) );
mpw_string_pushf( &out, "# Date: %s\n", mpw_default( "", mpw_marshal_data_get_str( data, "export", "date", NULL ) ) );
mpw_string_pushf( &out, "# User Name: %s\n", mpw_default( "", mpw_marshal_data_get_str( data, "user", "full_name", NULL ) ) );
mpw_string_pushf( &out, "# Full Name: %s\n", mpw_default( "", mpw_marshal_data_get_str( data, "user", "full_name", NULL ) ) );
mpw_string_pushf( &out, "# Avatar: %u\n", (unsigned int)mpw_marshal_data_get_num( data, "user", "avatar", NULL ) );
mpw_string_pushf( &out, "# Identicon: %s\n", mpw_marshal_data_get_str( data, "user", "identicon", NULL ) );
mpw_string_pushf( &out, "# Key ID: %s\n", mpw_marshal_data_get_str( data, "user", "key_id", NULL ) );
mpw_string_pushf( &out, "# Identicon: %s\n", mpw_default( "", mpw_marshal_data_get_str( data, "user", "identicon", NULL ) ) );
mpw_string_pushf( &out, "# Key ID: %s\n", mpw_default( "", mpw_marshal_data_get_str( data, "user", "key_id", NULL ) ) );
mpw_string_pushf( &out, "# Algorithm: %d\n", (MPAlgorithmVersion)mpw_marshal_data_get_num( data, "user", "algorithm", NULL ) );
mpw_string_pushf( &out, "# Default Type: %d\n", (MPResultType)mpw_marshal_data_get_num( data, "user", "default_type", NULL ) );
mpw_string_pushf( &out, "# Passwords: %s\n", mpw_marshal_data_get_bool( data, "export", "redacted", NULL )? "PROTECTED": "VISIBLE" );
@ -571,15 +571,15 @@ static const char *mpw_marshal_write_flat(
for (size_t s = 0; s < (sites? sites->children_count: 0); ++s) {
const MPMarshalledData *site = &sites->children[s];
mpw_string_pushf( &out, "%s %8ld %8s %25s\t%25s\t%s\n",
mpw_default( (char *)"", mpw_marshal_data_get_str( site, "last_used", NULL ) ),
mpw_default( "", mpw_marshal_data_get_str( site, "last_used", NULL ) ),
(long)mpw_marshal_data_get_num( site, "uses", NULL ),
mpw_str( "%lu:%lu:%lu",
(long)mpw_marshal_data_get_num( site, "type", NULL ),
(long)mpw_marshal_data_get_num( site, "algorithm", NULL ),
(long)mpw_marshal_data_get_num( site, "counter", NULL ) ),
mpw_default( (char *)"", mpw_marshal_data_get_str( site, "login_name", NULL ) ),
mpw_default( "", mpw_marshal_data_get_str( site, "login_name", NULL ) ),
site->obj_key,
mpw_default( (char *)"", mpw_marshal_data_get_str( site, "password", NULL ) ) );
mpw_default( "", mpw_marshal_data_get_str( site, "password", NULL ) ) );
}
if (!out)
@ -878,13 +878,15 @@ static void mpw_marshal_read_flat(
}
// Header
const char *line = positionInLine;
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) {
file->error = (MPMarshalError){
MPMarshalErrorStructure,
mpw_str( "Invalid header: %s", mpw_strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) )
mpw_str( "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) )
};
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
@ -956,7 +958,7 @@ static void mpw_marshal_read_flat(
str_counter = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersionAndCounter );
}
siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); // TODO: Needs to be encoded if redacted?
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteResultState = mpw_get_token( &positionInLine, endOfLine, "\n" );
break;
@ -999,9 +1001,9 @@ static void mpw_marshal_read_flat(
mpw_marshal_data_set_num( siteCounter, file->data, "sites", siteName, "counter", NULL );
mpw_marshal_data_set_num( siteAlgorithm, file->data, "sites", siteName, "algorithm", NULL );
mpw_marshal_data_set_num( siteType, file->data, "sites", siteName, "type", NULL );
mpw_marshal_data_set_str( siteResultState, file->data, "sites", siteName, "password", NULL );
mpw_marshal_data_set_str( siteResultState && strlen( siteResultState )? siteResultState: NULL, file->data, "sites", siteName, "password", NULL );
mpw_marshal_data_set_num( MPResultTypeDefault, file->data, "sites", siteName, "login_type", NULL );
mpw_marshal_data_set_str( siteLoginState, file->data, "sites", siteName, "login_name", NULL );
mpw_marshal_data_set_str( siteLoginState && strlen( siteLoginState )? siteLoginState: NULL, file->data, "sites", siteName, "login_name", NULL );
mpw_marshal_data_set_num( atoi( str_uses ), file->data, "sites", siteName, "uses", NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &siteLastUsed ) ))
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );

View File

@ -28,9 +28,6 @@ MP_LIBS_END
//// Types.
#define mpw_default(__default, __value) ({ __typeof__ (__default) _v = (__typeof__ (__default))(__value); _v = _v? _v: __default; })
#define mpw_default_n(__default, __num) ({ __typeof__ (__num) _n = (__num); !isnan( _n )? (__typeof__ (__default))_n: __default; })
typedef mpw_enum( unsigned int, MPMarshalFormat ) {
/** Do not marshal. */
MPMarshalFormatNone,
@ -77,58 +74,92 @@ MPMasterKeyProvider mpw_masterKeyProvider_str(const char *masterPassword);
void mpw_masterKeyProvider_free(void);
typedef struct MPMarshalError {
/** The status of the most recent processing operation. */
MPMarshalErrorType type;
/** An explanation of the situation that caused the current status type. */
const char *message;
} MPMarshalError;
typedef struct MPMarshalledData {
/** If the parent is an object, this holds the key by which this data value is referenced. */
const char *obj_key;
/** If the parent is an array, this holds the index at which this data value is referenced. */
size_t arr_index;
/** Whether this data value represents a null value (true). */
bool is_null;
/** Whether this data value represents a boolean value (true). */
bool is_bool;
/** The textual value of this data if it holds a string. */
const char *str_value;
/** The numerical value of this data if it holds a number or a boolean. */
double num_value;
/** Amount of data values references under this value if it represents an object or an array. */
size_t children_count;
/** Array of data values referenced under this value. */
struct MPMarshalledData *children;
} MPMarshalledData;
typedef struct MPMarshalledInfo {
/** The data format used for serializing the file and user data into a byte stream. */
MPMarshalFormat format;
/** Date of when the file was previously serialized. */
time_t exportDate;
/** Whether secrets and state should be visible in clear-text (false) when serialized. */
bool redacted;
/** Algorithm version to use for user operations (eg. key ID operations). */
MPAlgorithmVersion algorithm;
/** A number identifying the avatar to display for the user in this file. */
unsigned int avatar;
/** Unique name for this file's user, preferably the user's full legal name. */
const char *fullName;
/** User metadata: The identicon that was generated to represent this file's user identity. */
MPIdenticon identicon;
/** A unique identifier (hex) for the user's master key, primarily for authentication/verification. */
const char *keyID;
/** User metadata: Date of the most recent action taken by this user. */
time_t lastUsed;
} MPMarshalledInfo;
typedef struct MPMarshalledQuestion {
/** Unique name for the security question, preferably a single key word from the question sentence. */
const char *keyword;
/** The result type to use for generating an answer. */
MPResultType type;
/** State data (base64), if any, necessary for generating the question's answer. */
const char *state;
} MPMarshalledQuestion;
typedef struct MPMarshalledSite {
/** Unique name for this site. */
const char *siteName;
/** Algorithm version to use for all site operations (eg. result, login, question operations). */
MPAlgorithmVersion algorithm;
MPCounterValue counter;
/** The counter value of the site result to generate. */
MPCounterValue counter;
/** The result type to use for generating a site result. */
MPResultType resultType;
/** State data (base64), if any, necessary for generating the site result. */
const char *resultState;
/** The result type to use for generating a site login. */
MPResultType loginType;
/** State data (base64), if any, necessary for generating the site login. */
const char *loginState;
/** Site metadata: URL location where the site can be accessed. */
const char *url;
/** Site metadata: Amount of times an action has been taken for this site. */
unsigned int uses;
/** Site metadata: Date of the most recent action taken on this site. */
time_t lastUsed;
/** Amount of security questions associated with this site. */
size_t questions_count;
/** Array of security questions associated with this site. */
MPMarshalledQuestion *questions;
} MPMarshalledSite;
@ -136,21 +167,33 @@ typedef struct MPMarshalledUser {
MPMasterKeyProvider masterKeyProvider;
bool redacted;
/** A number identifying the avatar to display for this user. */
unsigned int avatar;
/** Unique name for this user, preferably the user's full legal name. */
const char *fullName;
/** User metadata: The identicon that was generated to represent this user's identity. */
MPIdenticon identicon;
/** Algorithm version to use for user operations (eg. key ID operations). */
MPAlgorithmVersion algorithm;
/** A unique identifier (hex) for the user's master key, primarily for authentication/verification. */
const char *keyID;
/** The initial result type to use for new sites created by the user. */
MPResultType defaultType;
/** User metadata: Date of the most recent action taken by this user. */
time_t lastUsed;
/** Amount of sites associated to this user. */
size_t sites_count;
/** Array of sites associated to this user. */
MPMarshalledSite *sites;
} MPMarshalledUser;
typedef struct MPMarshalledFile {
/** Metadata from the file that holds user data, available without the need for user authentication. */
MPMarshalledInfo *info;
/** All data in the file, including extensions and other data present, even if not used by this library. */
MPMarshalledData *data;
/** Status of parsing the file and any errors that might have occurred during the process. */
MPMarshalError error;
} MPMarshalledFile;

View File

@ -83,6 +83,9 @@ void mpw_log_ssink(LogLevel level, const char *file, int line, const char *funct
#define ftl(format, ...) MPW_LOG( LogLevelFatal, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#endif
//// Utilities
#ifndef min
#define min(a, b) ({ \
__typeof__ (a) _a = (a); \
@ -108,6 +111,10 @@ void mpw_log_ssink(LogLevel level, const char *file, int line, const char *funct
#define stringify_def(s) stringify(s)
#endif
#define mpw_default(__default, __value) ({ __typeof__ (__value) _v = __value; _v? _v: __default; })
#define mpw_default_n(__default, __num) ({ __typeof__ (__num) _n = (__num); !isnan( _n )? (__typeof__ (__default))_n: __default; })
//// Buffers and memory.
/** Write a number to a byte buffer using mpw's endianness (big/network endian). */