Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -1890,28 +1890,39 @@ void info_page(void){ const char *zName; Blob uuid; int rid; int rc; + int nLen; zName = P("name"); if( zName==0 ) fossil_redirect_home(); - if( validate16(zName, strlen(zName)) ){ - if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ - tktview_page(); - return; - } - if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){ - event_page(); - return; - } - } + nLen = strlen(zName); blob_set(&uuid, zName); + if( name_collisions(zName) ){ + cgi_set_parameter("src","info"); + ambiguous_page(); + return; + } rc = name_to_uuid(&uuid, -1, "*"); if( rc==1 ){ + if( validate16(zName, nLen) ){ + if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ + tktview_page(); + return; + } + if( db_exists("SELECT 1 FROM tag" + " WHERE tagname GLOB 'event-%q*'", zName) ){ + event_page(); + return; + } + } style_header("No Such Object"); @

No such object: %h(zName)

+ if( nLen<4 ){ + @

Objects of length < 4 are too ambiguous to worry about

+ } style_footer(); return; }else if( rc==2 ){ cgi_set_parameter("src","info"); ambiguous_page(); Index: src/name.c ================================================================== --- src/name.c +++ src/name.c @@ -266,11 +266,10 @@ } } return rid; } - /* ** This routine takes a user-entered UUID which might be in mixed ** case and might only be a prefix of the full UUID and converts it ** into the full-length UUID in canonical form. ** @@ -319,10 +318,39 @@ } return rid; } +/* +** name_collisions searches through events, blobs, and tickets for +** collisions of a given UUID based on its length on UUIDs no shorter +** than 4 characters in length. +*/ +int name_collisions(const char *zName){ + Stmt q; + int c = 0; /* count of collisions for zName */ + int nLen; /* length of zName */ + nLen = strlen(zName); + if( nLen>=4 && nLen<=UUID_SIZE && validate16(zName, nLen) ){ + db_prepare(&q, + "SELECT count(uuid) FROM" + " (SELECT substr(tkt_uuid, 1, %d) AS uuid FROM ticket" + " UNION ALL SELECT * FROM" + " (SELECT substr(tagname, 7, %d) FROM" + " tag WHERE tagname GLOB 'event-*')" + " UNION ALL SELECT * FROM" + " (SELECT substr(uuid, 1, %d) FROM blob))" + " WHERE uuid GLOB '%q*'" + " GROUP BY uuid HAVING count(uuid) > 1;", + nLen, nLen, nLen, zName); + if( db_step(&q)==SQLITE_ROW ){ + c = db_column_int(&q, 0); + } + db_finalize(&q); + } + return c; +} /* ** COMMAND: test-name-to-id ** ** Convert a name to a full artifact ID. @@ -406,10 +434,49 @@ int rid = db_column_int(&q, 1); @
  • @ %s(zUuid) - object_description(rid, 0, 0); @

  • + } + db_finalize(&q); + db_prepare(&q, + " SELECT tkt_rid, tkt_uuid, title" + " FROM ticket, ticketchng" + " WHERE ticket.tkt_id = ticketchng.tkt_id" + " AND tkt_uuid GLOB '%q*'" + " GROUP BY tkt_uuid" + " ORDER BY tkt_ctime DESC", z); + while( db_step(&q)==SQLITE_ROW ){ + int rid = db_column_int(&q, 0); + const char *zUuid = db_column_text(&q, 1); + const char *zTitle = db_column_text(&q, 2); + @
  • + @ %s(zUuid) - + @

    + @ Ticket + hyperlink_to_uuid(zUuid); + @ - %s(zTitle). + @ + @

  • + } + db_finalize(&q); + db_prepare(&q, + "SELECT rid, uuid FROM" + " (SELECT tagxref.rid AS rid, substr(tagname, 7) AS uuid" + " FROM tagxref, tag WHERE tagxref.tagid = tag.tagid" + " AND tagname GLOB 'event-%q*') GROUP BY uuid", z); + while( db_step(&q)==SQLITE_ROW ){ + int rid = db_column_int(&q, 0); + const char* zUuid = db_column_text(&q, 1); + @
  • + @ %s(zUuid) - + @

    + @

  • } @ db_finalize(&q); style_footer(); }