]> git.lyx.org Git - lyx.git/blob - src/support/linkback/LinkBackProxy.m
Fix bugs #6078 and #9364
[lyx.git] / src / support / linkback / LinkBackProxy.m
1 /**
2  * \file LinkBackProxy.m
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Stefan Schimanski
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "support/linkback/LinkBackProxy.h"
14
15 #include "support/linkback/LinkBack.h"
16
17 ///////////////////////////////////////////////////////////////////////
18
19 static NSAutoreleasePool * pool = nil;
20
21 @interface LyXLinkBackClient : NSObject <LinkBackClientDelegate> {
22         NSMutableSet * keys;
23 }
24
25 - (LyXLinkBackClient *)init;
26 - (BOOL)edit:(NSString *)fileName;
27 @end
28
29 @implementation LyXLinkBackClient
30
31 - (LyXLinkBackClient *)init
32 {
33         self = [super init];
34         if (self != nil)
35                 keys = [[NSMutableSet alloc] init];
36         return self;
37 }
38
39 - (void)dealloc {
40         // close all links
41         NSArray * allKeys = [keys allObjects];
42         unsigned i;
43         for (i = 0; i < [allKeys count]; ++i) {
44                 LinkBack * link
45                 = [LinkBack activeLinkBackForItemKey:[allKeys objectAtIndex:i]];
46                 [link closeLink];
47         }
48         [keys release];
49
50         [super dealloc];
51 }
52
53 - (BOOL)edit:(NSString *)fileName
54 {
55         if ([LinkBack activeLinkBackForItemKey:fileName])
56                 return YES;
57
58         @try {
59                 // get put data, i.e. PDF + LinkBack + 4 bytes PDF-length
60                 NSData * data = [NSData dataWithContentsOfFile:fileName];
61                 if (data == nil) {
62                         NSLog(@"Cannot read file %@", fileName);
63                         return NO;
64                 }
65                 
66                 // Get linkback data which comes behind the pdf data.
67                 // The pdf data length are the last 4 bytes.
68                 UInt32 pdfLen = 0;
69                 pdfLen = *(UInt32 const *)(((UInt8 const *)[data bytes]) + [data length] - 4);
70                 pdfLen = NSSwapBigLongToHost(pdfLen); // make it big endian
71                 if (pdfLen >= [data length] - 4) {
72                         NSLog(@"Invalid file %@ for LinkBack", fileName);
73                         return NO;
74                 }
75                         
76                 NSData * linkBackData 
77                 = [data subdataWithRange:NSMakeRange(pdfLen, [data length] - pdfLen - 4)]; 
78                 if (linkBackData == nil) {
79                         NSLog(@"Invalid file %@ for LinkBack", fileName);
80                         return NO;
81                 }
82                 
83                 NSMutableDictionary * linkBackDataDict
84                 = [NSUnarchiver unarchiveObjectWithData:linkBackData];
85                 if (linkBackDataDict == nil) {
86                         NSLog(@"LinkBack data in %@ corrupted", fileName);
87                         return NO;
88                 }
89                 
90                 // create the link to the LinkBack server
91                 LinkBack * link = [LinkBack editLinkBackData:linkBackDataDict
92                         sourceName:fileName delegate:self itemKey:fileName];
93                 if (link == nil) {
94                         NSLog(@"Failed to create LinkBack link for %@", fileName);
95                         return NO;
96                 }
97                 [keys addObject:fileName];
98         }
99         @catch (NSException * exception) {
100                 NSLog(@"LinkBack exception: %@", exception);
101                 return NO;
102         }
103         
104         return YES;
105 }
106
107 - (void)linkBackDidClose:(LinkBack*)link
108 {
109         NSString * fileName = [link itemKey];
110         if (fileName) {
111                 [keys removeObject:fileName];
112                 NSLog(@"LinkBack link for %@ closed", fileName);
113         }
114 }
115
116 - (void)linkBackServerDidSendEdit:(LinkBack*)link
117 {
118         @try {
119                 // get pasteboard and check that linkback and pdf data is available
120                 NSPasteboard * pboard = [link pasteboard];
121                 NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil];
122                 NSArray * pdfType = [NSArray arrayWithObjects: NSPDFPboardType, nil];
123                 if ([pboard availableTypeFromArray:linkBackType] == nil
124                     || [pboard availableTypeFromArray:pdfType] == nil) {
125                         NSLog(@"No PDF or LinkBack data in pasteboard");
126                         return;
127                 }
128                         
129                 // get new linkback data
130                 id linkBackDataDict = [pboard propertyListForType:LinkBackPboardType];
131                 if (linkBackDataDict == nil) {
132                         NSLog(@"Cannot get LinkBack data from pasteboard");
133                         return;
134                 }
135                 NSData * linkBackData = [NSArchiver archivedDataWithRootObject:linkBackDataDict];
136                 if (linkBackData == nil) {
137                         NSLog(@"Cannot archive LinkBack data");
138                         return;
139                 }
140                 
141                 // get pdf
142                 NSData * pdfData = [pboard dataForType:NSPDFPboardType];
143                 if (pdfData == nil) {
144                         NSLog(@"Cannot get PDF data from pasteboard");
145                         return;
146                 }
147                 
148                 // update the file
149                 NSString * fileName = [link itemKey];
150                 NSFileHandle * file = [NSFileHandle fileHandleForUpdatingAtPath:fileName];
151                 if (file == nil) {
152                         NSLog(@"LinkBack file %@ disappeared.", fileName);
153                         return;
154                 }
155                 [file truncateFileAtOffset:0];
156                 [file writeData:pdfData];
157                 [file writeData:linkBackData];
158                 
159                 UInt32 pdfLen = NSSwapHostLongToBig([pdfData length]); // big endian
160                 NSData * lenData = [NSData dataWithBytes:&pdfLen length:4];
161                 [file writeData:lenData];
162                 [file closeFile];
163         }
164         @catch (NSException * exception) {
165                 NSLog(@"LinkBack exception in linkBackServerDidSendEdit: %@", exception);
166         }
167 }
168
169 @end
170
171
172 ///////////////////////////////////////////////////////////////////////
173
174 static LyXLinkBackClient * linkBackClient = nil;
175
176 void checkAutoReleasePool()
177 {
178         if (pool == nil)
179                 pool = [[NSAutoreleasePool alloc] init];
180 }
181
182 int isLinkBackDataInPasteboard()
183 {
184         checkAutoReleasePool() ;
185         {
186                 NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil];
187                 NSString * ret = [[NSPasteboard generalPasteboard] availableTypeFromArray:linkBackType];
188                 return ret != nil;
189         }
190 }
191
192
193 void getLinkBackData(void const * * buf, unsigned * len)
194 {
195         checkAutoReleasePool() ;
196         {
197                 // get linkback data from pasteboard
198                 NSPasteboard * pboard = [NSPasteboard generalPasteboard];
199                 id linkBackData = [pboard propertyListForType:LinkBackPboardType];
200                 
201                 NSData * nsdata
202                 = [NSArchiver archivedDataWithRootObject:linkBackData];
203                 if (nsdata == nil) {
204                         *buf = 0;
205                         *len = 0;
206                         return;
207                 }
208
209                 *buf = [nsdata bytes];
210                 *len = [nsdata length];
211         }
212 }
213
214
215 int editLinkBackFile(char const * docName)
216 {
217         // setup Obj-C and our client
218         if (linkBackClient == nil)
219                 linkBackClient = [[LyXLinkBackClient alloc] init];
220         checkAutoReleasePool() ;
221         
222         // FIXME: really UTF8 here?
223         NSString * nsDocName = [NSString stringWithUTF8String:docName];
224         return [linkBackClient edit:nsDocName] == YES;
225 }
226
227
228 void closeAllLinkBackLinks()
229 {
230         if (linkBackClient != nil) {
231                 [linkBackClient release];
232                 linkBackClient = nil;
233         }
234
235         if (pool != nil) {
236                 [pool drain];
237                 pool = nil;
238         }
239 }
240