Skip to content

Commit ea358d4

Browse files
committed
Add tag gpg signatures to refreshReferences
1 parent 7139a9e commit ea358d4

1 file changed

Lines changed: 157 additions & 26 deletions

File tree

generate/templates/manual/repository/refresh_references.cc

Lines changed: 157 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class RefreshedRefModel {
6565
message(NULL),
6666
sha(new char[GIT_OID_HEXSZ + 1]),
6767
shorthand(strdup(git_reference_shorthand(ref))),
68+
tagOdbBuffer(NULL),
69+
tagOdbBufferLength(0),
6870
type(NULL)
6971
{
7072
if (git_reference_is_branch(ref)) {
@@ -76,33 +78,89 @@ class RefreshedRefModel {
7678
}
7779
}
7880

79-
static int fromReference(RefreshedRefModel **out, git_reference *ref) {
81+
static int fromReference(RefreshedRefModel **out, git_reference *ref, git_odb *odb) {
8082
RefreshedRefModel *refModel = new RefreshedRefModel(ref);
81-
git_oid referencedTargetOid;
83+
const git_oid *referencedTargetOid = git_reference_target(ref);
8284

83-
int result = getOidOfReferenceCommit(&referencedTargetOid, ref);
84-
if (result != GIT_OK) {
85-
delete refModel;
86-
return result;
85+
if (!git_reference_is_tag(ref)) {
86+
git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, referencedTargetOid);
87+
88+
*out = refModel;
89+
return GIT_OK;
8790
}
91+
git_repository *repo = git_reference_owner(ref);
8892

89-
if (git_reference_is_tag(ref)) {
90-
git_repository *repo = git_reference_owner(ref);
93+
git_tag *referencedTag;
94+
if (git_tag_lookup(&referencedTag, repo, referencedTargetOid) == GIT_OK) {
95+
refModel->message = strdup(git_tag_message(referencedTag));
9196

92-
git_tag *referencedTag;
93-
if (git_tag_lookup(&referencedTag, repo, &referencedTargetOid) == GIT_OK) {
94-
refModel->message = strdup(git_tag_message(referencedTag));
95-
git_tag_free(referencedTag);
97+
git_odb_object *tagOdbObject;
98+
if (git_odb_read(&tagOdbObject, odb, git_tag_id(referencedTag)) == GIT_OK) {
99+
refModel->tagOdbBufferLength = git_odb_object_size(tagOdbObject);
100+
refModel->tagOdbBuffer = new char[refModel->tagOdbBufferLength];
101+
std::memcpy(refModel->tagOdbBuffer, git_odb_object_data(tagOdbObject), refModel->tagOdbBufferLength);
102+
git_odb_object_free(tagOdbObject);
96103
}
104+
105+
git_tag_free(referencedTag);
106+
}
107+
108+
git_oid peeledReferencedTargetOid;
109+
int error = getOidOfReferenceCommit(&peeledReferencedTargetOid, ref);
110+
if (error != GIT_OK) {
111+
delete refModel;
112+
return error;
97113
}
98114

99-
git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, &referencedTargetOid);
115+
git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, &peeledReferencedTargetOid);
100116

101117
*out = refModel;
102118
return GIT_OK;
103119
}
104120

105-
v8::Local<v8::Object> toJavascript() {
121+
static void ensureSignatureRegexes() {
122+
if (!signatureRegexesBySignatureType.IsEmpty()) {
123+
return;
124+
}
125+
126+
v8::Local<v8::Array> gpgsigArray = Nan::New<v8::Array>(2),
127+
x509Array = Nan::New<v8::Array>(1);
128+
129+
Nan::Set(
130+
gpgsigArray,
131+
Nan::New<Number>(0),
132+
Nan::New<v8::RegExp>(
133+
Nan::New("-----BEGIN PGP SIGNATURE-----[\\s\\S]+?-----END PGP SIGNATURE-----").ToLocalChecked(),
134+
static_cast<v8::RegExp::Flags>(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline)
135+
).ToLocalChecked()
136+
);
137+
138+
Nan::Set(
139+
gpgsigArray,
140+
Nan::New<Number>(1),
141+
Nan::New<v8::RegExp>(
142+
Nan::New("-----BEGIN PGP MESSAGE-----[\\s\\S]+?-----END PGP MESSAGE-----").ToLocalChecked(),
143+
static_cast<v8::RegExp::Flags>(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline)
144+
).ToLocalChecked()
145+
);
146+
147+
Nan::Set(
148+
x509Array,
149+
Nan::New<Number>(0),
150+
Nan::New<v8::RegExp>(
151+
Nan::New("-----BEGIN SIGNED MESSAGE-----[\s\S]+?-----END SIGNED MESSAGE-----").ToLocalChecked(),
152+
static_cast<v8::RegExp::Flags>(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline)
153+
).ToLocalChecked()
154+
);
155+
156+
v8::Local<v8::Object> result = Nan::New<Object>();
157+
Nan::Set(result, Nan::New("gpgsig").ToLocalChecked(), gpgsigArray);
158+
Nan::Set(result, Nan::New("x509").ToLocalChecked(), x509Array);
159+
160+
signatureRegexesBySignatureType.Reset(result);
161+
}
162+
163+
v8::Local<v8::Object> toJavascript(v8::Local<v8::String> signatureType) {
106164
v8::Local<v8::Object> result = Nan::New<Object>();
107165

108166
v8::Local<v8::Value> jsFullName;
@@ -135,6 +193,34 @@ class RefreshedRefModel {
135193
}
136194
Nan::Set(result, Nan::New("shorthand").ToLocalChecked(), jsShorthand);
137195

196+
v8::Local<v8::Value> jsTagSignature = Nan::Null();
197+
if (tagOdbBuffer != NULL && tagOdbBufferLength != 0) {
198+
// tagOdbBuffer is already a copy, so we'd like to use NewBuffer instead,
199+
// but we were getting segfaults and couldn't easily figure out why. :(
200+
// We tried passing the tagOdbBuffer directly to NewBuffer and then nullifying tagOdbBuffer so that
201+
// the destructor didn't double free, but that still segfaulted internally in Node.
202+
v8::Local<v8::Object> buffer = Nan::CopyBuffer(tagOdbBuffer, tagOdbBufferLength).ToLocalChecked();
203+
v8::Local<v8::Value> toStringProp = Nan::Get(buffer, Nan::New("toString").ToLocalChecked()).ToLocalChecked();
204+
v8::Local<v8::Object> jsTagOdbObjectString = Nan::CallAsFunction(toStringProp->ToObject(), buffer, 0, NULL).ToLocalChecked()->ToObject();
205+
206+
v8::Local<v8::Object> _signatureRegexesBySignatureType = Nan::New(signatureRegexesBySignatureType);
207+
v8::Local<v8::Array> signatureRegexes = v8::Local<v8::Array>::Cast(Nan::Get(_signatureRegexesBySignatureType, signatureType).ToLocalChecked());
208+
209+
for (uint32_t i = 0; i < signatureRegexes->Length(); ++i) {
210+
v8::Local<v8::Value> argv[] = {
211+
Nan::Get(signatureRegexes, Nan::New(i)).ToLocalChecked()
212+
};
213+
214+
v8::Local<v8::Value> matchProp = Nan::Get(jsTagOdbObjectString, Nan::New("match").ToLocalChecked()).ToLocalChecked();
215+
v8::Local<v8::Value> match = Nan::CallAsFunction(matchProp->ToObject(), jsTagOdbObjectString, 1, argv).ToLocalChecked();
216+
if (match->IsArray()) {
217+
jsTagSignature = Nan::Get(match->ToObject(), 0).ToLocalChecked();
218+
break;
219+
}
220+
}
221+
}
222+
Nan::Set(result, Nan::New("tagSignature").ToLocalChecked(), jsTagSignature);
223+
138224
v8::Local<v8::Value> jsType;
139225
if (type == NULL) {
140226
jsType = Nan::Null();
@@ -151,12 +237,17 @@ class RefreshedRefModel {
151237
if (message != NULL) { delete[] message; }
152238
delete[] sha;
153239
if (shorthand != NULL) { delete[] shorthand; }
240+
if (tagOdbBuffer != NULL) { delete[] tagOdbBuffer; }
154241
}
155242

156-
char *fullName, *message, *sha, *shorthand;
243+
char *fullName, *message, *sha, *shorthand, *tagOdbBuffer;
244+
size_t tagOdbBufferLength;
157245
const char *type;
246+
static Nan::Persistent<v8::Object> signatureRegexesBySignatureType;
158247
};
159248

249+
Nan::Persistent<v8::Object> RefreshedRefModel::signatureRegexesBySignatureType;
250+
160251
class UpstreamModel {
161252
public:
162253
UpstreamModel(const char *inputDownstreamFullName, const char *inputUpstreamFullName):
@@ -277,7 +368,25 @@ class RefreshReferencesData {
277368

278369
NAN_METHOD(GitRepository::RefreshReferences)
279370
{
280-
if (info.Length() == 0 || !info[0]->IsFunction()) {
371+
v8::Local<v8::String> signatureType;
372+
if (info.Length() == 2) {
373+
if (!info[0]->IsString()) {
374+
return Nan::ThrowError("Signature type must be \"gpgsig\" or \"x509\".");
375+
}
376+
377+
v8::Local<v8::String> signatureTypeParam = info[0]->ToString();
378+
if (
379+
Nan::Equals(signatureTypeParam, Nan::New("gpgsig").ToLocalChecked()) != Nan::Just(true)
380+
&& Nan::Equals(signatureTypeParam, Nan::New("x509").ToLocalChecked()) != Nan::Just(true)
381+
) {
382+
return Nan::ThrowError("Signature type must be \"gpgsig\" or \"x509\".");
383+
}
384+
signatureType = signatureTypeParam;
385+
} else {
386+
signatureType = Nan::New("gpgsig").ToLocalChecked();
387+
}
388+
389+
if (info.Length() == 0 || (info.Length() == 1 && !info[0]->IsFunction()) || (info.Length() == 2 && !info[1]->IsFunction())) {
281390
return Nan::ThrowError("Callback is required and must be a Function.");
282391
}
283392

@@ -291,6 +400,7 @@ NAN_METHOD(GitRepository::RefreshReferences)
291400
Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[0]));
292401
RefreshReferencesWorker *worker = new RefreshReferencesWorker(baton, callback);
293402
worker->SaveToPersistent("repo", info.This());
403+
worker->SaveToPersistent("signatureType", signatureType);
294404
Nan::AsyncQueueWorker(worker);
295405
return;
296406
}
@@ -302,6 +412,18 @@ void GitRepository::RefreshReferencesWorker::Execute()
302412
LockMaster lockMaster(true, baton->repo);
303413
git_repository *repo = baton->repo;
304414
RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out;
415+
git_odb *odb;
416+
417+
baton->error_code = git_repository_odb(&odb, repo);
418+
if (baton->error_code != GIT_OK) {
419+
if (giterr_last() != NULL) {
420+
baton->error = git_error_dup(giterr_last());
421+
}
422+
git_odb_free(odb);
423+
delete refreshData;
424+
baton->out = NULL;
425+
return;
426+
}
305427

306428
// START Refresh HEAD
307429
git_reference *headRef = NULL;
@@ -311,17 +433,19 @@ void GitRepository::RefreshReferencesWorker::Execute()
311433
if (giterr_last() != NULL) {
312434
baton->error = git_error_dup(giterr_last());
313435
}
436+
git_odb_free(odb);
314437
delete refreshData;
315438
baton->out = NULL;
316439
return;
317440
}
318441

319442
RefreshedRefModel *headModel;
320-
baton->error_code = RefreshedRefModel::fromReference(&headModel, headRef);
443+
baton->error_code = RefreshedRefModel::fromReference(&headModel, headRef, odb);
321444
if (baton->error_code != GIT_OK) {
322445
if (giterr_last() != NULL) {
323446
baton->error = git_error_dup(giterr_last());
324447
}
448+
git_odb_free(odb);
325449
git_reference_free(headRef);
326450
delete refreshData;
327451
baton->out = NULL;
@@ -336,7 +460,7 @@ void GitRepository::RefreshReferencesWorker::Execute()
336460
// START Refresh CHERRY_PICK_HEAD
337461
git_reference *cherrypickRef = NULL;
338462
if (lookupDirectReferenceByShorthand(&cherrypickRef, repo, "CHERRY_PICK_HEAD") == GIT_OK) {
339-
baton->error_code = RefreshedRefModel::fromReference(&refreshData->cherrypick, cherrypickRef);
463+
baton->error_code = RefreshedRefModel::fromReference(&refreshData->cherrypick, cherrypickRef, odb);
340464
git_reference_free(cherrypickRef);
341465
} else {
342466
cherrypickRef = NULL;
@@ -347,7 +471,7 @@ void GitRepository::RefreshReferencesWorker::Execute()
347471
git_reference *mergeRef = NULL;
348472
// fall through if cherry pick failed
349473
if (baton->error_code == GIT_OK && lookupDirectReferenceByShorthand(&mergeRef, repo, "MERGE_HEAD") == GIT_OK) {
350-
baton->error_code = RefreshedRefModel::fromReference(&refreshData->merge, mergeRef);
474+
baton->error_code = RefreshedRefModel::fromReference(&refreshData->merge, mergeRef, odb);
351475
git_reference_free(mergeRef);
352476
} else {
353477
mergeRef = NULL;
@@ -358,6 +482,7 @@ void GitRepository::RefreshReferencesWorker::Execute()
358482
if (giterr_last() != NULL) {
359483
baton->error = git_error_dup(giterr_last());
360484
}
485+
git_odb_free(odb);
361486
delete refreshData;
362487
baton->out = NULL;
363488
return;
@@ -371,6 +496,7 @@ void GitRepository::RefreshReferencesWorker::Execute()
371496
if (giterr_last() != NULL) {
372497
baton->error = git_error_dup(giterr_last());
373498
}
499+
git_odb_free(odb);
374500
delete refreshData;
375501
baton->out = NULL;
376502
return;
@@ -383,6 +509,7 @@ void GitRepository::RefreshReferencesWorker::Execute()
383509
if (giterr_last() != NULL) {
384510
baton->error = git_error_dup(giterr_last());
385511
}
512+
git_odb_free(odb);
386513
git_strarray_free(&referenceNames);
387514
delete refreshData;
388515
baton->out = NULL;
@@ -424,14 +551,15 @@ void GitRepository::RefreshReferencesWorker::Execute()
424551
}
425552

426553
RefreshedRefModel *refreshedRefModel;
427-
baton->error_code = RefreshedRefModel::fromReference(&refreshedRefModel, reference);
554+
baton->error_code = RefreshedRefModel::fromReference(&refreshedRefModel, reference, odb);
428555
git_reference_free(reference);
429556

430557
if (baton->error_code == GIT_OK) {
431558
refreshData->refs.push_back(refreshedRefModel);
432559
}
433560
}
434561

562+
git_odb_free(odb);
435563
git_strarray_free(&remoteNames);
436564
git_strarray_free(&referenceNames);
437565

@@ -449,6 +577,7 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback()
449577
{
450578
if (baton->out != NULL)
451579
{
580+
RefreshedRefModel::ensureSignatureRegexes();
452581
RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out;
453582
v8::Local<v8::Object> result = Nan::New<Object>();
454583

@@ -458,27 +587,29 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback()
458587
Nan::New<String>(refreshData->headRefFullName).ToLocalChecked()
459588
);
460589

590+
v8::Local<v8::String> signatureType = GetFromPersistent("signatureType")->ToString();
591+
461592
unsigned int numRefs = refreshData->refs.size();
462-
v8::Local<v8::Array> refs = Nan::New<Array>(numRefs);
593+
v8::Local<v8::Array> refs = Nan::New<v8::Array>(numRefs);
463594
for (unsigned int i = 0; i < numRefs; ++i) {
464595
RefreshedRefModel *refreshedRefModel = refreshData->refs[i];
465-
Nan::Set(refs, Nan::New<Number>(i), refreshedRefModel->toJavascript());
596+
Nan::Set(refs, Nan::New(i), refreshedRefModel->toJavascript(signatureType));
466597
}
467598
Nan::Set(result, Nan::New("refs").ToLocalChecked(), refs);
468599

469600
unsigned int numUpstreamInfo = refreshData->upstreamInfo.size();
470-
v8::Local<v8::Array> upstreamInfo = Nan::New<Array>(numUpstreamInfo);
601+
v8::Local<v8::Array> upstreamInfo = Nan::New<v8::Array>(numUpstreamInfo);
471602
for (unsigned int i = 0; i < numUpstreamInfo; ++i) {
472603
UpstreamModel *upstreamModel = refreshData->upstreamInfo[i];
473-
Nan::Set(upstreamInfo, Nan::New<Number>(i), upstreamModel->toJavascript());
604+
Nan::Set(upstreamInfo, Nan::New(i), upstreamModel->toJavascript());
474605
}
475606
Nan::Set(result, Nan::New("upstreamInfo").ToLocalChecked(), upstreamInfo);
476607

477608
if (refreshData->cherrypick != NULL) {
478609
Nan::Set(
479610
result,
480611
Nan::New("cherrypick").ToLocalChecked(),
481-
refreshData->cherrypick->toJavascript()
612+
refreshData->cherrypick->toJavascript(signatureType)
482613
);
483614
} else {
484615
Nan::Set(result, Nan::New("cherrypick").ToLocalChecked(), Nan::Null());
@@ -488,7 +619,7 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback()
488619
Nan::Set(
489620
result,
490621
Nan::New("merge").ToLocalChecked(),
491-
refreshData->merge->toJavascript()
622+
refreshData->merge->toJavascript(signatureType)
492623
);
493624
} else {
494625
Nan::Set(result, Nan::New("merge").ToLocalChecked(), Nan::Null());

0 commit comments

Comments
 (0)