Compare commits

...

4 Commits

Author SHA1 Message Date
duffyduck ba0b79de64 Release v0.0.0.9 2026-04-03 12:14:42 +02:00
duffyduck 60bd3163a9 Add debug logging for raw Starface API contact data
Logs the first contact from Starface API response into the sync
log so we can see the actual JSON structure and fix field mapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:13:43 +02:00
duffyduck a3a3ac1dcc Release v0.0.0.8 2026-04-03 12:10:35 +02:00
duffyduck 9d58a1a113 Fix Starface JSON parsing and Outlook folder/contact reading
Starface API:
- Handle both array and object responses from /contacts endpoint
- Try common wrapper fields (items, contacts, data, results)

Outlook contacts:
- Add FindFolderByPath that matches exact FolderPath property
- Fallback to Namespace.Folders navigation if store lookup fails
- Fallback: try reading contact fields even if Class != 40
- Add debug logging for folder path and item count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:08:50 +02:00
6 changed files with 155 additions and 42 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
; Erfordert Inno Setup 6.x (https://jrsoftware.org/isinfo.php) ; Erfordert Inno Setup 6.x (https://jrsoftware.org/isinfo.php)
#define MyAppName "Starface Outlook Sync" #define MyAppName "Starface Outlook Sync"
#define MyAppVersion "0.0.0.7" #define MyAppVersion "0.0.0.9"
#define MyAppPublisher "HackerSoft - Hacker-Net Telekommunikation" #define MyAppPublisher "HackerSoft - Hacker-Net Telekommunikation"
#define MyAppURL "https://www.hacker-net.de" #define MyAppURL "https://www.hacker-net.de"
#define MyAppExeName "StarfaceOutlookSync.exe" #define MyAppExeName "StarfaceOutlookSync.exe"
@@ -153,6 +153,32 @@ namespace StarfaceOutlookSync.Services
} }
} }
private dynamic FindFolderByPath(dynamic folder, string targetPath)
{
try
{
string currentPath = folder.FolderPath;
if (currentPath == targetPath)
return folder;
var subs = folder.Folders;
for (int i = 1; i <= (int)subs.Count; i++)
{
try
{
var sub = subs[i];
var match = FindFolderByPath(sub, targetPath);
if (match != null) return match;
Marshal.ReleaseComObject(sub);
}
catch { }
}
Marshal.ReleaseComObject(subs);
}
catch { }
return null;
}
private dynamic GetFolderByPath(string folderPath) private dynamic GetFolderByPath(string folderPath)
{ {
var app = GetOutlookApp(); var app = GetOutlookApp();
@@ -163,51 +189,69 @@ namespace StarfaceOutlookSync.Services
try try
{ {
var parts = folderPath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); // Versuch 1: Alle Kontaktordner durchsuchen und per FolderPath matchen
dynamic current = null;
var stores = ns.Stores; var stores = ns.Stores;
for (int i = 1; i <= (int)stores.Count; i++) for (int i = 1; i <= (int)stores.Count; i++)
{ {
var store = stores[i]; try
var root = store.GetRootFolder();
string rootName = root.Name;
if (rootName == parts[0])
{ {
current = root; var store = stores[i];
break; var root = store.GetRootFolder();
var match = FindFolderByPath(root, folderPath);
if (match != null)
{
Marshal.ReleaseComObject(stores);
return match;
}
Marshal.ReleaseComObject(root);
Marshal.ReleaseComObject(store);
} }
Marshal.ReleaseComObject(root); catch { }
Marshal.ReleaseComObject(store);
} }
Marshal.ReleaseComObject(stores); Marshal.ReleaseComObject(stores);
if (current == null) // Versuch 2: Namespace.Folders direkt navigieren
return ns.GetDefaultFolder(OlFolderContacts); var parts = folderPath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1)
for (int i = 1; i < parts.Length; i++)
{ {
bool found = false; var topFolders = ns.Folders;
var subFolders = current.Folders; for (int i = 1; i <= (int)topFolders.Count; i++)
for (int j = 1; j <= (int)subFolders.Count; j++)
{ {
var sub = subFolders[j]; var topFolder = topFolders[i];
if ((string)sub.Name == parts[i]) if ((string)topFolder.Name == parts[0])
{ {
current = sub; dynamic current = topFolder;
found = true; for (int p = 1; p < parts.Length; p++)
break; {
bool found = false;
var subs = current.Folders;
for (int j = 1; j <= (int)subs.Count; j++)
{
var sub = subs[j];
if ((string)sub.Name == parts[p])
{
current = sub;
found = true;
break;
}
Marshal.ReleaseComObject(sub);
}
Marshal.ReleaseComObject(subs);
if (!found) { current = null; break; }
}
if (current != null)
{
Marshal.ReleaseComObject(topFolders);
return current;
}
} }
Marshal.ReleaseComObject(sub); Marshal.ReleaseComObject(topFolder);
} }
Marshal.ReleaseComObject(subFolders); Marshal.ReleaseComObject(topFolders);
if (!found)
return ns.GetDefaultFolder(OlFolderContacts);
} }
return current; System.Diagnostics.Debug.WriteLine($"Folder not found by path: {folderPath}, using default");
return ns.GetDefaultFolder(OlFolderContacts);
} }
catch catch
{ {
@@ -221,24 +265,51 @@ namespace StarfaceOutlookSync.Services
try try
{ {
var folder = GetFolderByPath(folderPath); var folder = GetFolderByPath(folderPath);
var items = folder.Items; System.Diagnostics.Debug.WriteLine($"Reading contacts from: {(string)folder.FolderPath}");
for (int i = 1; i <= (int)items.Count; i++) var items = folder.Items;
int count = (int)items.Count;
System.Diagnostics.Debug.WriteLine($"Items count: {count}");
for (int i = 1; i <= count; i++)
{ {
dynamic item = null; dynamic item = null;
try try
{ {
item = items[i]; item = items[i];
// Nur ContactItems verarbeiten (Class = 40 = olContact) int itemClass = -1;
if ((int)item.Class == 40) try { itemClass = (int)item.Class; } catch { }
// olContact = 40, aber manche Kontakte melden Class anders
// Versuch einfach die Kontakt-Felder zu lesen
if (itemClass == 40)
{ {
contacts.Add(MapFromOutlook(item)); contacts.Add(MapFromOutlook(item));
} }
else
{
// Fallback: Pruefen ob es trotzdem ein Kontakt ist
try
{
string eid = item.EntryID;
string fn = item.FirstName;
string ln = item.LastName;
// Hat Kontakt-Felder -> ist ein Kontakt
contacts.Add(MapFromOutlook(item));
}
catch
{
// Kein Kontakt, ueberspringen
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error reading item {i}: {ex.Message}");
} }
catch { }
finally finally
{ {
if (item != null) Marshal.ReleaseComObject(item); if (item != null) try { Marshal.ReleaseComObject(item); } catch { }
} }
} }
@@ -155,11 +155,14 @@ namespace StarfaceOutlookSync.Services
return books; return books;
} }
public event Action<string> OnDebug;
public async Task<List<UnifiedContact>> GetContactsAsync(StarfaceAddressBook book) public async Task<List<UnifiedContact>> GetContactsAsync(StarfaceAddressBook book)
{ {
var contacts = new List<UnifiedContact>(); var contacts = new List<UnifiedContact>();
int page = 0; int page = 0;
const int pageSize = 200; const int pageSize = 200;
bool firstPage = true;
while (true) while (true)
{ {
@@ -172,9 +175,47 @@ namespace StarfaceOutlookSync.Services
var resp = await _http.GetAsync($"{_baseUrl}/contacts?{query}"); var resp = await _http.GetAsync($"{_baseUrl}/contacts?{query}");
if (!resp.IsSuccessStatusCode) break; if (!resp.IsSuccessStatusCode) break;
var array = JArray.Parse(await resp.Content.ReadAsStringAsync()); var body = await resp.Content.ReadAsStringAsync();
JArray array;
// Die API gibt je nach Version ein Array oder ein Objekt mit "items" zurueck
var token = JToken.Parse(body);
if (token is JArray directArray)
{
array = directArray;
}
else if (token is JObject obj)
{
// Versuche gaengige Felder: items, contacts, data, results
array = (obj["items"] ?? obj["contacts"] ?? obj["data"] ?? obj["results"]) as JArray;
if (array == null)
{
// Einzelnes Kontakt-Objekt? Dann in Array wrappen
if (obj["id"] != null && obj["blocks"] != null)
{
array = new JArray { obj };
}
else
{
System.Diagnostics.Debug.WriteLine($"Unerwartete Starface-Antwort: {body.Substring(0, Math.Min(200, body.Length))}");
break;
}
}
}
else
{
break;
}
if (array.Count == 0) break; if (array.Count == 0) break;
// Ersten Kontakt als Debug-Info loggen
if (firstPage && array.Count > 0)
{
OnDebug?.Invoke($"Starface API Rohdaten (1. Kontakt):\n{array[0].ToString(Formatting.Indented)}");
firstPage = false;
}
foreach (var item in array) foreach (var item in array)
contacts.Add(MapFromStarface(item)); contacts.Add(MapFromStarface(item));
@@ -53,6 +53,7 @@ namespace StarfaceOutlookSync.Services
Log("Verbinde mit Starface..."); Log("Verbinde mit Starface...");
using (var starface = new StarfaceApiClient(profile.StarfaceConnection)) using (var starface = new StarfaceApiClient(profile.StarfaceConnection))
{ {
starface.OnDebug += (msg) => Log(msg);
var loginOk = await starface.LoginAsync(); var loginOk = await starface.LoginAsync();
if (!loginOk) if (!loginOk)
{ {
@@ -7,9 +7,9 @@
<AssemblyTitle>Starface Outlook Sync</AssemblyTitle> <AssemblyTitle>Starface Outlook Sync</AssemblyTitle>
<Company>HackerSoft - Hacker-Net Telekommunikation</Company> <Company>HackerSoft - Hacker-Net Telekommunikation</Company>
<Product>Starface Outlook Sync</Product> <Product>Starface Outlook Sync</Product>
<Version>0.0.0.7</Version> <Version>0.0.0.9</Version>
<AssemblyVersion>0.0.0.7</AssemblyVersion> <AssemblyVersion>0.0.0.9</AssemblyVersion>
<FileVersion>0.0.0.7</FileVersion> <FileVersion>0.0.0.9</FileVersion>
<Description>Synchronisiert Outlook-Kontakte mit Starface Telefonanlage</Description> <Description>Synchronisiert Outlook-Kontakte mit Starface Telefonanlage</Description>
<Copyright>Stefan Hacker - HackerSoft</Copyright> <Copyright>Stefan Hacker - HackerSoft</Copyright>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
+1 -1
View File
@@ -27,7 +27,7 @@ namespace StarfaceOutlookSync.UI
var lblVersion = new Label var lblVersion = new Label
{ {
Text = "Version 0.0.0.7", Text = "Version 0.0.0.9",
Left = 0, Top = 56, Width = 340, Height = 20, Left = 0, Top = 56, Width = 340, Height = 20,
TextAlign = ContentAlignment.MiddleCenter, TextAlign = ContentAlignment.MiddleCenter,
ForeColor = Color.Gray ForeColor = Color.Gray