Gustavo Sied > Sin categoría > Dirigiéndose a la libreta de direcciones iOS6 y SQLite Trampas
Dirigiéndose a la libreta de direcciones iOS6 y SQLite Trampas
Gustavo
6 febrero, 2014
Ahora que tengo un mango básica sobre el Sms.db iOS6, es hora de mirar a la libreta de direcciones iOS6. Después de todo, ahora sé lo que se dice cuando examino la Sms.db, pero no disfrutar de una auténtica imagen buena, que no sea el número de teléfono, de que ha de enviar el mensaje. El iOS AddressBook.sqlitedb es el lugar para buscar datos relacionados con el número de teléfono
Una breve mirada a AddressBook.sqlitedb
La base de datos iOS6 AddressBook.sqlitedb tiene 29 mesas:.
ABAccount ; ABPersonFullTextSearch_segdir
ABGroup ABPersonFullTextSearch_segments
ABGroupChanges ABPersonFullTextSearch_stat
ABGroupMembers ; ABPersonLink
ABMultiValue ABPersonMultiValueDeletes
ABMultiValueEntry ABPersonSearchKey
ABMultiValueEntryKey ; ABPhoneLastFour
ABMultiValueLabel ABRecent
ABPerson ; ABStore
ABPersonBasicChanges FirstSortSectionCount
ABPersonChanges ; FirstSortSectionCountTotal
ABPersonFullTextSearch» color: blue ; font-family: Courier New, Courier, espacio sencillo; font-size: x-small; «> ABPersonFullTextSearch_content LastSortSectionCountTotal
ABPersonFullTextSearch_docsize _SqliteDatabaseProperties
El mesa abperson parece ser el objetivo obvio para los datos que queremos. Aquí está su esquema:
CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY autoincrement, primer texto, Última TEXT, TEXT Media, TEXTO FirstPhonetic, MiddlePhonetic texto, texto LastPhonetic, Organización TEXT, TEXT Departamento, nota de texto, tipo INTEGER, TEXT Cumpleaños, JobTitle texto, texto Alias, prefijo de texto, TEXTO sufijo, TEXTO FirstSort, LastSort TEXTO, CreationDate INTEGER, INTEGER modificationDate, CompositeNameFallback TEXTO, ExternalIdentifier TEXTO, ExternalModificationTag texto, texto ExternalUUID, StoreID INTEGER, DisplayName TEXTO, ExternalRepresentation BLOB, FirstSortSection TEXTO, LastSortSection TEXTO, FirstSortLanguageIndex INTEGER DEFAULT 2147483647, LastSortLanguageIndex INTEGER DEFAULT 2147483647, PersonLink INTEGER DEFAULT -1, ImageURI TEXTO, IsPreferredName INTEGER DEFAULT 1)
Si la molestia de leer a través de eso, se dará cuenta de no hay nada acerca de los números de teléfono o direcciones de correo electrónico. Tenemos que encontrar la tabla que contiene los números de teléfono, y de alguna manera unirse a ese a esta tabla que tiene los datos de nombres.
Echa un vistazo a una muestra (ficticio) fila:
; ROWID = 1
Primero = Some
Última = Nombre
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> FirstPhonetic =
MiddlePhonetic =
LastPhonetic =
Organización = SomeCompany
= Departamento
Nota =
Cumpleaños =
JobTitle =
Apodo =
Sufijo =
FirstSort = – ‘- 1’ 7 = W + ‘= 17I /? ) ‘MM’ = 7CA 57/1
LastSort = 1 ‘7 = W -‘ – + ‘= 17I /)’ MM ‘= 7CA 57/1
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> modificationDate = 369882045
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> ExternalIdentifier =
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> ExternalUUID = 68516E99 -7C39-4c4d-8871-BDE114EDD6B4
DisplayName =
ExternalRepresentation =
FirstSortSection = –
LastSortSection = 1
FirstSortLanguageIndex = 0
LastSortLanguageIndex = 0
PersonLink = -1
ImageURI =
IsPreferredName = 1
Un poquito más, nos encontramos con la tabla ABMultivalue contiene números de teléfono, direcciones de correo electrónico, direcciones URL a sitios de redes sociales, y potencialmente más. Sólo hay seis campos (lo que es un poco más fácil en los ojos), pero hay un pequeño problema. La lectura de la tabla plana (es decir, que no están relacionadas con ninguna otra tabla) no sabemos a quien los números y direcciones de correo electrónico pertenecen, ni sabemos qué tipo de datos que es. Por ejemplo, ¿es una casa, trabajo, móvil o número de teléfono FAX? Eche un vistazo a su maquillaje y algunas filas de ejemplo y verá lo que quiero decir:
CREATE TABLE ABMultiValue (UID INTEGER PRIMARY KEY, INTEGER record_id, INTEGER propiedad, INTEGER identificador, etiqueta INTEGER, TEXT value)
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> record_id = 1
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> identificador = 0 blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> valor = [email protected]
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> UID = 1026
blue; font-family: Courier New, Courier, monospace; font-size: x-small;»> property = 3 label = 1
valor = (# # #) # # # – # # # #
Eso es un poco más fácil en los ojos. Se puede ver que el campo «valor» al final de la fila contiene los números de teléfono y direcciones de correo electrónico que estamos buscando. El problema viene del hecho de que es el único campo de texto en la tabla. Todos los demás valores son enteros. Entonces, ¿cómo nos relacionamos con los nombres de la tabla ABPerson a los números en la ABMultivalue? Lo hice bastante obvio aquí, pero ABperson.ROWID = ABMultivalue.record_id
Así que, ¿qué pasa con ese otro problema:. Qué tipo de datos es que ? Aquí viene la tabla ABMultiValueLabel al rescate! Echa un vistazo a esto:
Valor = $ _ [CASA] $ _
valor = Móvil 1
valor = DAD
valor = MOM
Si eso parece un poco extraño, es porque el usuario iOS puede crear sus propias etiquetas. Algunos, como Móvil 1 y mobile2, van a ser por defecto. Otros, como mamá y papá son personalizado. Pero algo más debe captar su atención, también: no hay valores en la tabla ABMultiValueLabel con las que coincida con el número entero de etiqueta en la mesa ABMultivalue! ¿Y ahora qué?
introduje la SQLite campo ROWID en un artículo anterior. En resumen, todas las mesas SQLite tiene un campo rowid, que es un entero único, si es o no se especificó cuando se creó la tabla. En el caso de la tabla ABMultiValueLabel, el rowid es la relación con el entero de etiqueta en la mesa ABMultivalue para informarnos del tipo de datos. Observar:
$ sqlite3-line ‘select rowid, el valor de abmultivaluelabel donde rowid = 1 o rowid = 3’ AddressBook.sqlitedb
rowid = 1 valor = iPhone
rowid = 3 valor = _ $ [Inicio] $ _
Ahora que sabemos cómo se interrelacionan estas tablas, podemos componer una consulta para generar una lista de contactos simples.
Un Simple Contact List
Para recapitular rápidamente, las tablas ABPerson, ABMultivalue y ABMultivalueLabel son interrelacionados: ABPerson contiene los nombres de los contactos, ABMultivalue alberga los números de teléfono, direcciones de correo electrónico y posiblemente otros valores (por ejemplo, direcciones URL), y ABMultivalueLabel define los valores. Podemos crear una lista de contactos, ordenados por número de identificación de contactos, de esta manera:
$ sqlite3-header AddressBook.sqlitedb ‘select p.rowid, p.first, p.last, p.organization, l.value como Tipo, m.value de abperson p, abmultivalue m, l donde abmultivaluelabel p.rowid = m.record_id y l.rowid = m.label orden p.rowid; ‘
ROWID | Primera | Última | Organización | Tipo | Valor
1 | Algunas | chico | SomeCompany | _ $ [Inicio] $ _ | [email protected]
1 | Algunas | chico | SomeCompany | iPhone | (# # #) # # # – # # # #
(Nota: Sigue leyendo Aunque tal vez no sea aparente, esta consulta anterior! es errónea.)
Comprobación de nuestro trabajo
Mientras que la consulta anterior funciona muy bien, no devuelve todos los datos disponibles en las tablas. Echemos un vistazo algunos datos de base de datos y usted verá lo que quiero decir:
$ sqlite3 ‘select count (*) del abperson;’ AddressBook.sqlitedb
542
$ sqlite3 ‘select count (*) del abmultivalue;’ AddressBook.sqlitedb
$ sqlite3 AddressBook.sqlitedb ‘select count (*) del abmultivaluelabel;’
14
El ABPerson tabla tiene 542 entradas. Es de esperar que al menos 542 líneas en nuestra consulta, suponiendo un número de teléfono, correo electrónico u otro valor en la tabla ABMultivalue. Considerando que hay 1588 filas ABMultivalue, que en realidad esperamos una media de tres filas por contacto. Pero, ¿cuántas filas se devuelven con nuestra consulta «Simple Contact List»
$ sqlite3 recuento AddressBook.sqlitedb ‘select (*) del abperson p, abmultivalue m, l abmultivaluelabel donde p.rowid = m.record_id y l. rowid = m.label; ‘
$ sqlite3 AddressBook.sqlitedb ‘select count (*) del abperson donde ROWID no en (seleccione record_id de abmultivalue);’
0
No, todos los 542 ROWIDs de ABPerson se encuentran en los record_ids de ABMultivalue. Ciertamente queremos todos los registros ABPerson representados en nuestra producción, y claramente no estamos obteniendo con la consulta como escrita. Pero, ¿cuántos de los registros ABMultivalue se refieren a los registros ABPerson
$ sqlite3 ‘select count (*) del abmultivalue donde record_id en (seleccione rowid de abperson);’ AddressBook.sqlitedb
1588
Todos ellos. Así que, ahora que sabemos la partitura: nuestra consulta no será correcto hasta que tengamos 1.588 filas de datos. Así que, ¿dónde está la avería? Para averiguarlo, tenemos que mirar cuidadosamente nuestra búsqueda original:
seleccione # aquí tenemos un solo seleccionar las columnas que deseamos
p.rowid,
p.first,
blue; font-family:’Courier New’, Courier, monospace; font-size: x-small;»> p.organization,
blue; font-family:’Courier New’, Courier, monospace; font-size: x-small;»> m.value
# Aquí la lista de las tablas de origen y asignamos un alias
abperson p,
abmultivalue m,
abmultivaluelabel l
donde # aquí vamos a filtrar los datos, y esta es nuestra zona de peligro
blue; font-family:’Courier New’, Courier, monospace; font-size: x-small;»> y l.rowid = m.label
clasificar por # aquí simplemente estamos pidiendo los resultados
p.rowid; ‘
Así, parecería que que concentrarse en la cláusula donde el que tiene el efecto de filtrar nuestros datos. Estamos haciendo dos cosas aquí:
- p.rowid = m.record_id – rowid de ABPerson debe coincidir con un record_id ABMultivalue, que ya hemos mostrado ocurre en todos los casos
- l.rowid = m.label – Debe haber una rowid coincidente en ABMultivalueLabel para cada etiqueta ABMultivalue
Parece que la segunda declaración en la cláusula where es nuestro culpable. Podemos demostrar que este es el caso y verifique nuestra evaluación de la consulta original
$ sqlite3 AddressBook.sqlitedb ‘select count (*) del abperson, abmultivalue donde rowid = record_id;’
$ sqlite3 AddressBook.sqlitedb ‘select p.rowid, p.first, p.last, p.organization, m.label como Tipo, m.value de abperson p, m abmultivalue donde p.rowid = m.record_id; ‘| wc-l
1588
lo tanto, era el filtro final que resultó en la reducción de los datos establecer. Eso significa que debe haber etiquetas no definidos en la tabla ABMultivalue (y de hecho, 1.283 de los registros tienen valores de etiqueta NULL)! Así que, ¿podemos eliminar ese filtro de nuestra consulta original y lo llaman bueno
$ sqlite3 AddressBook.sqlitedb ‘select p.rowid, p.first, p.last, p.organization, l.value como Tipo, m . valor de abperson p, abmultivalue m, l abmultivaluelabel donde p.rowid = m.record_id orden p.rowid; ‘| wc-l
22233
¿Qué? ! Se devuelven más de 22.000 filas? ¿Cómo funciona que a pasar? Es muy sencillo, pero también es fácilmente mal entendido. Sin nuestro último filtro, nuestro selecto declaración dice para mostrar el campo de valor ABMultivaluelabel. Hay 14 filas de esa tabla, por lo que quedan 14 filas para cada uno de los 1.588 registros de la tabla ABMultivalue. Yo no soy un genio de las matemáticas, pero 542 1588 +14 = … dos más ocho más cuatro es catorce, llevar el … no está cerca de 22K!
Así que necesito una manera de mostrar el nombre de la etiqueta si existe en la tabla ABMultivaluelabel, de lo contrario mostrar la etiqueta entera ABMultivalue. Incluso si no sabemos lo que significa, que no queremos pasar por alto una entrada ABMutlivalue porque podemos llegar a comprender su significado a través de otra fase de nuestra investigación. . Podemos lograr esto con un par de sentencias select anidadas en la instrucción de selección primaria
seleccione
p.rowid,
p.first, p.last,
p.organization,
caso
cuando m.label en (seleccione rowid de abmultivaluelabel)